Slack interactive buttons fail in message from incoming webhook - button

I have created a Slack app with an incoming webhook and slash command. The OAUTH process works as expected and I am able to successfully deploy the app, retrieve and store the app token.
I have created and sent a message (with interactive buttons) into Slack, via the app's incoming webhook. The problem is that the buttons do not operate, and generate an error message within the Slack channel.
Having read as much of the Slack docs as I can find, I note that bots sending in messages to Slack are required to include the app token in the message. I read the webhook docs in detail however and could not find a similar requirement. The webhook guide just mentions the JSON format needed but nothing re the app token. Am I missing something? The guide clearly states that webhook messages can include interactive buttons, yet the button just doesn't work. I'm creating the response as follows:
$actions = [
[
"name" => "save",
"text" => "Save",
"type" => "button",
"value" => "save"
]
];
$attachments = [
[
"fallback" => "fallback message",
"title" => "Attachment 1",
"text" => "foobar attachment",
"color" => "#0066ff",
"callback_id" => "btn_action",
"actions" => $actions
]
];
$payload = [
"channel" => "#test",
"response_type" => "ephemeral",
"icon_emoji" => ":rocket:",
"username" => "Test User",
"attachments" => $attachments
];
$data = 'payload=' . json_encode($payload);
$ch = curl_init($webhook);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
Would love any advice about how to resolve!
Cheers,
Andrew.

I had this same problem and it was due to using a slack token that was different from the one generated when I authorized my app. I expect that slack can only identify the app if it has bot token that is unique to that app. Unfortunately, it was a non-trivial process to get this new bot access token. I'll outline it below:
Select your app and the 'bot' checkbox in the "Add the Slack button"
section of https://api.slack.com/docs/slack-button.
Paste the url it generates in the 'a' element into your browser and then authorize yourself.
Collect the code token that it adds to the url when it tries to redirect back to your application.
Use the code token to fetch the bot_access_token for your app. I already had a python app using slack for oath-based login so I just modified that, but with python slackclient the code would look like this:
SlackClient("your api token").api_call(
'oauth.access',
client_id="your app client id",
client_secret="your app client secret",
code="the code token")
I think it works this way because 3rd-party integrations just store the bot_access_token per team and use it later. In my case, I just wanted to stuff the bot access token an environment variable for my app, which will never be in the public slack app directory. I'd love to know about an easier way to get a bot access token, so please let me know if you find one. Hope this works for you, Andrew.

Related

Push Notifications in Perl

I am implementing push notifications for a site that has a Perl back end. Firebase is the push notification service i am using. I have spent a fair bit of time with this and looked at a number of guides and some useful resources on SO. I have come up with a working implementation with just one issue. The problem is when send out a push notification it seems to arrive on the client/browser as an empty message. That is no data containing the 'title' and 'body' is retrievable on the client/browser side when the push notification arrives.
I have tried using both firebases older and newer api and either way it ends up with the same outcome of empty push notifications arriving on the client/browser. I have tested this on chrome,firefox and android and the same thing happens.
Here is the perl code that sends the push notification. I have excluded generating the bearer token to limit how much code there is to read.
#SEND PUSH NOTIFICATION
my $push_subscriber = <get subscriber details from db>
my $end_point_host = $push_subscriber->{endpoint};
my $end_point = "https://$end_point_host/v1/projects/<my project
id>/messages:send";
my $request = HTTP::Request->new('POST',$end_point);
$request->header('Authorization'=>"Bearer $bearer_token");
$request->header('Content-Type' => 'application/json');
$request->content(JSON::encode_json ({
message => {
token => $push_subscriber->{subscription_id},
notification => {
title => 'test',
body => 'test content'
},
webpush => {
headers => {
Urgency => 'high'
},
notification => {
body => 'test content',
requireInteraction => 'true'
}
}
}}));
#send the request
$ua->request($request));
Here is the client/browser side javascript that is called when a push notification arrives. This is inside service-worker.js
self.addEventListener('push', function(e) {
var body;
if (e.data) {//THE PROBLEM IS HERE. No 'data' object exists
body = e.data.text();
} else {
body = "Empty Message";
}
var options = {
body: body
};
e.waitUntil(
self.registration.showNotification('My Notification', options)
);
});
The point where the problem presents itself is pointed out in the above javascript. Any help/feedback would be much appreciated. Thanks.
I ended up getting this working by just re-writing my client side subscription code. In my case the bell icon subscription on/off button along with all the js code to make it work.
Basically i went from using googles solution to a firebase specific solution with this guide.
https://firebase.google.com/docs/cloud-messaging/js/receive
You only need to store the 'token' on your server and the endpoint is always - https://fcm.googleapis.com/v1/projects/YOUR PROJECT ID/messages:send
The firebase guide contains a sample file where you can subscribe/unsubscribe for push notifications.
https://github.com/firebase/quickstart-js/blob/4be200b1c55616535159365b74bfd1fc128c1ebf/messaging/index.html
Once i had this working i could then cut it down and re-write it into just a simple notification button.
For some reason the provided firebase-messaging-sw.js from the guide didn't work for me but using service-worker.js shown in my OP did and so i can now receive push notifications along with their title, body and other data.
This here is how i generate the bearer token used in my OP sample perl code to send out a push notification.
Google API OAuth2 : “invalid_grant” error when requesting token for Service Account with Perl
That should hopefully cover everything you need to know if you are wanting to do push notifications on a site with a Perl back end. Hopefully this helps someone else wanting to do the same thing.

What is the correct format and sequence to acquire the correct token for LinkedIn API V2?

Our LinkedIn APP no longer works with the evolution of V2. I have tried a couple of times and failed to create the correct token. I am seeking help to create the correct authorization link to get the token. Error from the App is currently "Empty oauth2 access token"
I created a new LinkedIn app to replace our old one. I have tried to follow the instructions from LinkedIn and Microsoft but my efforts have produced the following error
My most recent attempt was:
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=78xaqf0ereiisy&redirect_uri=https://www.gachina.com&state=gachina&scope=r_emailaddress r_liteprofile w_member_social
I received: https://www.gachina.com/?code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&state=gachina
then I used the format shown here:
https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code={authorization_code_from_step2_response}&redirect_uri=hhttps%3A%2F%2Fdev.example.com%2Fauth%2Flinkedin%2Fcallback&client_id={your_client_id}&client_secret={your_client_secret}
with
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&redirect_uri=https://www.gachina.com/auth/Linkedin/callback&client_id=78xaqf0ereiisy&client_secret={client_secret}
but I receive the following upon submitting the above link with our {client secret} in place
{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}
I am doing all of this within minutes. So, I do not believe there is an expiration of code.
Can you help identify the error of steps or code to receive a Token?
This will return the access token
$params = array('grant_type' => 'authorization_code',
'client_id' => $this->api_key,
'client_secret' => $this->api_secret,
'code' => $_GET['code'],
'redirect_uri' => base_url().$this->redirect,
);
// Access Token request
$url = 'https://www.linkedin.com/oauth/v2/accessToken?' . http_build_query($params);
$data_len = strlen(http_build_query($params));
// Tell streams to make a POST request
$context = stream_context_create(
array('http' =>
array('method' => 'POST','header'=> 'Content-Length: 0'
)
)
);
// Retrieve access token information
$response = file_get_contents($url, false, $context);
$token = json_decode($response);
return $token->access_token;

Custom wordpress login with API Rest and angular

is there a way to create a Custom login with the API Rest of Wordpress and angular.
Currently I'm using the WP REST API - OAuth 1.0a Server plugin but I can´t figure out how to do it
Or maybe its posible using the two methods (Basic Authentication and OAuth)?
I would appreciate any help
I have been wrestling with this the past couple weeks. It kind of depends on your use case.
First, don't use Basic Auth. It's insecure and for development only. Not worth the time to set up.
OAuth (I think) is for when you already have a repository of users somewhere, and those users want to give your app approval to access their info, create an account for them, etc. Think of a "Login with Faceook!" button or something, that's OAuth. Could be wrong but I don't think that's what you want.
What I landed on, and what I think you are asking for, was JWT or JSON Web Token Auth. This is best for me because I want users to be able to create new user accounts and login to them completely within the app.
First, install the JWT Authentication for WP-API plugin:
https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/
This will expose a new endpoint for JWT authentication in the REST API. You will ping that endpoint with user credentials, and get a token response. You then store that token somehow (I'm currently using localStorage) and append it to the request headers of every request that requires permissions. De facto you are logged in! See the plugin docs for details. The example code for attaching the request is in AngJS, not Ang2/4, but the concept is the same. Here's an example from a service that posts a comment.
postComment(comment): any {
let headers = new Headers({ 'Authorization': 'Bearer ' + JSON.parse(localStorage.getItem('currentUser')).token});
let options = new RequestOptions({ headers: headers });
return this.http
.post(this._wpBase + "comments", comment, options)
.subscribe((res: Response) => {
res.json();
});
}
There is probably a fancier, global way to do this but I am still figuring it all out. Hope this is helpful!
Paste Following code in your themes function.php file.
Make sure that WP-REST-API plugin Should be installed on wordpress site
add_action( 'rest_api_init', 'register_api_hooks' );
function register_api_hooks() {
register_rest_route(
'custom-plugin', '/login/',
array(
'methods' => 'GET',
'callback' => 'login',
)
);
}
function login($request){
$creds = array();
$creds['user_login'] = $request["username"];
$creds['user_password'] = $request["password"];
$creds['remember'] = true;
$user = wp_signon( $creds, false );
if ( is_wp_error($user) )
echo $user->get_error_message();
return $user;
}
add_action( 'after_setup_theme', 'custom_login' );
Then your API will be created as
http://www.url.com/wp-json/custom-plugin/login?username=xyz&password=xyz
Try it with Postman You will get 200 as a response and user info

Can't make OAuth work in Drupal 6 / Services 3

I'm working on a project and the client requested i use OAuth to protect some custom services I've built. I've got everything working perfectly using no authentication and session authentication, but using OAuth makes the whole thing fall apart. I haven't been able to find any really good step by step tutorials.
Basically I've got the Services 6.x-3.1 and OAuth 6.x-3.0-beta4 modules installed on D6. I created an oauth context using this documentation. Then I setup a dummy user because I only want to create one set of consumer credentials for all of my clients. I assigned the context to the user and to the service. I'm using http://mydomain/oauth/authorized as the callback url.
To test I connect using http://term.ie/oauth/example/client.php. I use the endpoint http://mydomain/oauth/request_token. I add the response tokens to the appropriate fields. Then I change the endpoint to http://mydomain/oauth/authorize. It takes me to a page where I have to click to allow the connection. I'm taken to the http://mydomain/oauth/authorized page where I get a success message.
Now I change the endpoint to http://mydomain/oauth/access_token and I get a response with a new token. According to these instructions, I replace the new access tokens and change the endpoint to http://mydomain/myserviceendpoint. It connects using a GET request and returns
401 Unauthorized: The consumer is not authorized to access this
service.
It feels like I'm missing something. There is only one context in the system and it's assigned to both the consumer and the service. The consumer key and secret are part of the final url along with the access token. I've looked at permissions and I can't find anything out of place. I even changed my service to just return TRUE for the hook_services_access to bypass permissions.
Any ideas?
First you must understand the difference between the two.
first the session authentication takes a user name and password and authenticates. Then it takes the user permissions from drupal so you need to authenticate in each step to go through your application that communicate with your drupal. Anonymous users can get what they want according to your permission in drupal.
While in Aouth authentication you create a user and add token to him and only the user who had the token communicate with the application according to the permission rule you set to him . In case of anonymous users they can't retrieve or get anything. The communication between drupal and the client within the created used with a certain token.
Finally: goto user page and create a user and give him the application name also token and in your application code give the token to application and authenticate with the newly created user.
You must check your permissions to the newly created user.
Check this link
Same problem like you... As there are not really good tutorials, sometimes its hard, so hope that this helps someone.
Solution:
You have to give oauth permisions to your resources in your endpoint definition, in my_module_default_services_endpoint(). Let's say you want to enable resource user via oauth. Normally you would have:
...
$endpoint->resources['user'] = array(
'operations' => array(
'ta_create' => array('enabled' => 1, ),
),
'actions' => array(
'login' => array('enabled' => 1, ),
'logout' => array('enabled' => 1, ),
'ta_register' => array('enabled' => 1, ),
),
);
...
So you have to authorize your resources like this ('user_info' is the permision required to access this resource):
...
$endpoint->resources['user'] = array(
'operations' => array(
'ta_create' => array('enabled' => 1, 'services_oauth' => array('credentials' => 'token', 'authorization' => 'user_info', ), ),
),
'actions' => array(
'login' => array('enabled' => 1, ),
'logout' => array('enabled' => 1, 'services_oauth' => array('credentials' => 'token', 'authorization' => 'user_info', ), ),
'ta_register' => array('enabled' => 1, ),
),
);
...
The ones you enable via 'services_oauth' will be only accessible via oauth. I'm wondering if you can mix oauth access services and normal oauth services.
How did I find the solution:
If you enable services debugging, you will notice the following PHP warning:
Notice: Undefined index: services_oauth en _services_oauth_authenticate_call() (línea 26 de C:/.../services_oauth.inc).
If you go to _services_oauth_authenticate_call() in services_oauth.inc and print the stack trace (thanks to http://php.net/manual/en/function.debug-print-backtrace.php):
ob_start();
debug_print_backtrace();
$trace = ob_get_contents();
ob_end_clean();
watchdog('my_module', 'StackTrace: %st', array('%st' => $trace,), WATCHDOG_DEBUG);
Then you will see that *$method['endpoint']['services_oauth'];* is not set. So you have to set it in my_module_default_services_endpoint()
Your service endpoint is not to be used with the OAuth test client. Once you are authenticated, you pass your OAuth tokens to the service endpoint along with your method.

PHP SDK: How do I capture the access token after user auths app?

This is for a canvas app on the Facebook Platform using the new(est) Facebook PHP SDK.
We are using the PHP example from the Facebook tutorial (https://developers.facebook.com/docs/appsonfacebook/tutorial/) to trigger the OAuth dialog and get the test user to the redirect URL.
At the redirect URL, we use the PHP example from the Facebook signed request docs page (https://developers.facebook.com/docs/authentication/signed_request/) and our test users can successfully authorize the app.
However, after the test user auths the app, we are not able to capture the access token and its expiration. We can see it in the address bar appended to the redirect URL, but it does not show up in the $_REQUEST array. If we add {$access_token = $facebook->getAccessToken();} to the redirect URL page, it shows a value for the access token, but the value it shows is not the full token string that we see when we click on Show Token in the Test User Roles page (which we believe is the correct access token for the test user).
Here is an example of the redirect URL with an access token appended:
http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php#access_token=126736467765%7C2.AQDavId8oL80P5t9.3600.1315522800.1-100002908746828%7CJICJwM1P_97tKmqkEO5pXDCf-7Y&expires_in=6008
Here is what var_dump shows for the $REQUEST array for that same page:
array(3) { ["_qca"]=> string(26) "P0-709927483-1291994912966" ["__switchTo5x"]=> string(2) "30" ["PHPSESSID"]=> string(26) "euois02ead39ijumca7nffblh2" }
We have no idea why the $_REQUEST array varies from the values appended to the URL, and more importantly -- how to capture the access token and its expiration date.
Can someone show us a working example of how they capture this data after running the parse_signed_request($signed_request, $secret) function on the redirect page? Thanks!
ADDITIONAL INFO:
Here is the pertinent code from A) our test index page, and B) our test redirect page. If we use our text index page as the redirect url it gets stuck in an endless loop -- because the user is never identified.
A) Index Page
// Create kk-fb app instance
$facebook = new Facebook(array(
'appId' => KKFB_ID,
'secret' => KKFB_KY,
'oauth' => true,
));
$app_id = KKFB_ID;
$secret = KKFB_KY;
$canvas_auth = 'http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php';
$auth_url = "https://www.facebook.com/dialog/oauth?"
. "client_id=" . $app_id
. "&redirect_uri=" . urlencode($canvas_auth)
. "&response_type=token"
. "&scope=email,publish_stream";
$signed_request = $_REQUEST["signed_request"];
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
if (empty($data["user_id"])) {
echo("<script> top.location.href='" . $auth_url . "'</script>");
} else {
echo ("Welcome User: " . $data["user_id"]);
}
B) Redirect Page
// Create kk-fb app instance
$facebook = new Facebook(array(
'appId' => KKFB_ID,
'secret' => KKFB_KY,
'oauth' => true,
));
$app_id = KKFB_ID;
$secret = KKFB_KY;
$signed_request = $_REQUEST["signed_request"];
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
$user = $facebook->getUser();
$access_token = $facebook->getAccessToken();
echo "User: $user <br>";
echo "Access Token: $access_token <br>";
echo "Signed Request: $signed_request <br>";
var_dump($_REQUEST);
Here is what shows up as these echo results:
User: 0
Access Token: 126736467765|**SECRET**
Signed Request:
array(3) { ["_qca"]=> string(26) "P0-709927483-1291994912966" ["_switchTo5x"]=> string(2) "30" ["PHPSESSID"]=> string(26) "frugi545cdl15gjind1fnv6pq1" }
Interestingly, when the test user goes back to the index page the if condition is satisfied and we can get the correct access token:
Welcome User: 100002908746828
Access Token: 126736467765|2.AQBgcyzfu75IMCjw.3600.1315544400.1-100002908746828|m5IYEm976tJAkbTLdxHAhhgKmz8
Obviously, we are still missing something!? Also, we need to learn how to get the expiration time as a variable too so we can store both of these in our database.
OK, let's try this again.
Server-side vs Client-side Authentication
You are exclusively using the PHP SDK, so you want to do server-side authentication, where the authentication code is sent to the server over HTTP via the URL. This will allow you to fetch an access token for the user on the first page load after auth (in your case, the redirect page). The auth_url you are currently constructing is setting response_type=token, which forces the redirect to use client-side auth mode and set the token in the URL fragment instead of in the query. You should remove that parameter completely. In fact, I highly recommend you just use the PHP SDK instead of constructing that URL yourself. See example below.
Application Access Tokens
The odd-looking access token 126736467765|SECRET is your application access token, which is composed of your app ID and secret key. The application access token is returned by getAccessToken() if no user access token is available (because some API calls require at least some sort of access token). This also means that you've revealed your secret key to the world via this blog post, so you should reset your app secret otherwise anyone will be able to make API calls on your behalf. I highly recommend you elide parts of your access tokens if you share them with others.
Token Expiration
The OAuth 2.0 flow and v3.1.1 of the PHP SDK don't make determining the expiration time of a token all that easy. I would suggest attempting to make the API call, and then refreshing the token if the API call fails with an OAuthException. Tokens can be invalid even if they haven't expired, so this deals with more cases. However, if you still want to maintain the expiration date on your end, you might just want to extract it from the token itself. If you have an expiring token, then the expiration timestamp will be contained within that string. Here's a function I put together quickly to extract that:
function extractExpirationFromToken($access_token) {
$segments = explode('|', $access_token);
if(count($segments) < 2) { return 0; }
$segments = explode('.', $segments[1]);
if(count($segments) < 4) { return 0; }
$expires = $segments[3];
$dash_pos = strrpos($expires, '-');
if($dash_pos !== false) {
$expires = substr($expires, 0, $dash_pos);
}
return $expires;
}
New Index Page Code
// Create kk-fb app instance
$facebook = new Facebook(array(
'appId' => KKFB_ID,
'secret' => KKFB_KY,
));
$canvas_auth = 'http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php';
$auth_url = $facebook->getLoginUrl(array(
'scope' => 'email,publish_stream',
'redirect_uri' => $canvas_auth, // you could just redirect back to this index page though
));
$user = $facebook->getUser();
if (empty($user)) {
echo("<script> top.location.href='" . $auth_url . "'</script>");
} else {
echo ("Welcome User: " . $user);
}
Redirect Page
I don't think you need this page at all. You could just redirect the user back to your original index page.
// Create kk-fb app instance
$facebook = new Facebook(array(
'appId' => KKFB_ID,
'secret' => KKFB_KY,
));
$user = $facebook->getUser();
$access_token = $facebook->getAccessToken();
// also copy the function definition given earlier
$expiration = extractExpirationFromToken($access_token);
echo "User: $user <br>";
echo "Access Token: $access_token <br>";
echo "Expiration: $expiration <br>";
echo "Request: <br>";
var_dump($_REQUEST);
You can use the facebook build in method getAccessToken() for example;
$access_token = $facebook->getAccessToken();
This will give you the access token to your variable, now if you are getting it empty, remember to first check if the fuid is being properly catch, if it isn't you might need to review your settings be sure your "App Domain" is set this part is very important after setting it correctly you need to reset your app secret, then set your new values in your auth code. Hope this help, let me know :)
pd. Also remember to keep the scope of your variables visible in your whole php file or class.
Problem
The access_token in your pasted URL is not part of the query string, but instead contained in the URL fragment (after the #). URL fragments are not sent to the web server, and are readable only by client-side code like Javascript. Therefore the PHP SDK only sees http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php, which is why $_REQUEST does not contain an access_token key.
Questions / Notes
What are you using for your redirect_uri? I think you want to be using something like http://apps.facebook.com/your_canvas_url/
You shouldn't need to call parse_signed_request yourself or copy any code from the signed request page. The PHP SDK will do that for you. Just call:
$facebook = new Facebook(array(
'appId' => '…',
'secret' => '…',
));
$access_token = $facebook->getAccessToken();
Possible solutions
Also use the Facebook Javascript SDK. You can start by adding its <script> tag in your destination page (kk-fb-auth.php) (see the docs for full details; don't forget to set oauth: true). The JS SDK should set a cookie (named fbsr_126736467765) which the PHP SDK will be able to read via $_REQUEST or $_COOKIE on subsequent page loads.
If you want to do this with PHP, you can get the user's access token with a separate call to the Graph API at your redirect_uri. For this you need to change the response_type of your $auth_url in your index page to "code" or "code token".
Then, at your redirect page, Facebook will add a "code" parameter in the querystring. This API call will return you the full access_token and expiration time:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&
redirect_uri=YOUR_URL&
client_secret=YOUR_APP_SECRET&
code=$_REQUEST['code']
For more information you can refer to the docs on authentication.

Resources