Is there an API method in Slack-Api to set (change) Events API Request URLs so I can do this in code? - slack-api

To use Events API for Slack App development, there is a setting for "Events API Request URLs" as described in doc:
In the Events API, your Events API Request URL is the target location
where all the events your application is subscribed to will be
delivered, regardless of the team or event type.
There is a UI for changing the URL "manually" at api.slack.com under
"Event Subscriptions" section in settings. There is also url_verification event after changing the Request URL described here.
My question - Is there an API call (method) so I can update the endpoint (Request URL) from my server code?
For example, in Facebook API there is a call named subscriptions where I can change webhook URL after initial setup - link
Making a POST request with the callback_url, verify_token, and object
fields will reactivate the subscription.
PS. To give a background, this is needed for development using outbound tunnel with dynamic endpoint URL, e.g. ngrok free subscription. By the way, ngrok is referenced in sample "onboarding" app by slack here
Update. I checked Microsoft Bot Framework, and they seems to use RTM (Real Time Messaging) for slack which doesn't require Request URL setup, and not Events API. Same time, e.g. for Facebook they (MS Bot) instruct me to manually put their generated URL to webhook settings of a FB app, so there is no automation on that.

Since this question was originally asked, Slack has introduced app manifests, which enable API calls to change app configurations. This can be used to update URLs and other parameters, or create/delete apps.
At the time of writing, the manifest / manifest API is in beta:
Beta API — this API is in beta, and is subject to change without the usual notice period for changes.
so the this answer might not exactly fit the latest syntax as they make changes.
A programatic workflow might look as follows:
Pull a 'template' manifest from an existing version of the application, with most of the settings as intended (scopes, name, etc.)
Change parts of the manifest to meet the needs of development
Verify the manifest
Update a slack app or create a new one for testing
API List
Basic API list
Export a manifest as JSON: apps.manifest.export
Validate a manifest JSON: apps.manifest.validate
Update an existing app: apps.manifest.update
Create a new app from manifest: apps.manifest.create
Delete an app: apps.manifest.delete
Most of these API requests are Tier 1 requests, so only on the order of 1+ per minute.
API Access
You'll need to create and maintain "App Configuration Tokens". They're created in the "Your Apps" dashboard. More info about them here.
Example NodeJS Code
const axios = require('axios');
// Change these values:
const TEMPLATE_APP_ID = 'ABC1234XYZ';
const PUBLIC_URL = 'https://www.example.com/my/endpoint';
let access = {
slackConfigToken: "xoxe.xoxp-1-MYTOKEN",
slackConfigRefreshToken: "xoxe-1-MYREFRESHTOKEN",
slackConfigTokenExp: 1648550283
};
// Helpers ------------------------------------------------------------------------------------------------------
// Get a new access token with the refresh token
async function refreshTokens() {
let response = await axios.get(`https://slack.com/api/tooling.tokens.rotate?refresh_token=${access.slackConfigRefreshToken}`);
if (response.data.ok === true) {
access.slackConfigToken = response.data.token;
access.slackConfigRefreshToken = response.data.refresh_token;
access.slackConfigTokenExp = response.data.exp;
console.log(access);
} else {
console.error('> [error] The token could not be refreshed. Visit https://api.slack.com/apps and generate tokens.');
process.exit(1);
}
}
// Get an app manifest from an existing slack app
async function getManifest(applicationID) {
const config = {headers: { Authorization: `Bearer ${access.slackConfigToken}` }};
let response = await axios.get(`https://slack.com/api/apps.manifest.export?app_id=${applicationID}`, config);
if (response.data.ok === true) return response.data.manifest;
else {
console.error('> [error] Invalid could not get manifest:', response.data.error);
process.exit(1);
}
}
// Create a slack application with the given manifest
async function createDevApp(manifest) {
const config = {headers: { Authorization: `Bearer ${access.slackConfigToken}` }};
let response = await axios.get(`https://slack.com/api/apps.manifest.create?manifest=${encodeURIComponent(JSON.stringify(manifest))}`, config);
if (response.data.ok === true) return response.data;
else {
console.error('> [error] Invalid could not create app:', response.data.error);
process.exit(1);
}
}
// Verify that a manifest is valid
async function verifyManifest(manifest) {
const config = {headers: { Authorization: `Bearer ${access.slackConfigToken}` }};
let response = await axios.get(`https://slack.com/api/apps.manifest.validate?manifest=${encodeURIComponent(JSON.stringify(manifest))}`, config);
if (response.data.ok !== true) {
console.error('> [error] Manifest did not verify:', response.data.error);
process.exit(1);
}
}
// Main ---------------------------------------------------------------------------------------------------------
async function main() {
// [1] Check token expiration time ------------
if (access.slackConfigTokenExp < Math.floor(new Date().getTime() / 1000))
// Token has expired. Refresh it.
await refreshTokens();
// [2] Load a manifest from an existing slack app to use as a template ------------
const templateManifest = await getManifest(TEMPLATE_APP_ID);
// [3] Update URLS and data in the template ------------
let devApp = { name: 'Review App', slashCommand: '/myslashcommand' };
templateManifest.settings.interactivity.request_url = `${PUBLIC_URL}/slack/events`;
templateManifest.settings.interactivity.message_menu_options_url = `${PUBLIC_URL}/slack/events`;
templateManifest.features.slash_commands[0].url = `${PUBLIC_URL}/slack/events`;
templateManifest.oauth_config.redirect_urls[0] = `${PUBLIC_URL}/slack/oauth_redirect`;
templateManifest.settings.event_subscriptions.request_url = `${PUBLIC_URL}/slack/events`;
templateManifest.display_information.name = devApp.name;
templateManifest.features.bot_user.display_name = devApp.name;
templateManifest.features.slash_commands[0].command = devApp.slashCommand;
// [5] Verify that the manifest is still valid ------------
await verifyManifest(templateManifest);
// [6] Create our new slack dev application ------------
devApp.data = await createDevApp(templateManifest);
console.log(devApp);
}
main();
Hope this helps anyone else looking to update Slack applications programatically.

No, such a method does not exist in the official documentation. There might be an unofficial method - there are quite a few of them actually - but personally I doubt it.
But you don't need this feature for developing Slack apps. Just simulate the POST calls from Slack on your local dev machine with a script and then do a final test together with Slack on your webserver on the Internet.

Related

How to create SendBird user with SendBird Platform API and Request node library

We are building a react-native chat app. We are implementing a back end authentication solution on google Firebase. The creation of a new user in Firebase Auth triggers a cloud function which should create a new SendBird user with an access token. The access token will be stored in Cloud Firestore, ready for retrieval the next time the user logs in.
We are having trouble implementing the POST request that creates the new user via the platform API. We are using the Request library for node.js. We are able to reach the API endpoint, but the following object is returned: { message: 'SendBird API Endpoint.', error: true }. There is no indication of what the error may be.
This happens when sending the request to the base url. When we send the request to /users or /v3/users, we receive a 403 error.
Any indication as to what may be causing this problem would be greatly appreciated.
Below is the cloud function index.js code
const functions = require('firebase-functions');
const admin = require("firebase-admin");
const request = require('request');
admin.initializeApp();
exports.handleNewUser = functions.auth.user().onCreate((user) => {
var newUserRequestBody = {
"user_id": user.email,
"nickname": user.email,
"profile_url": "",
"issue_access_token": true,
}
request.post({
headers: {
'Content-Type': 'application/json, charset=utf8',
'Api-Token': // API Token
},
url: 'https://api-{application_id}.sendbird.com',
form: newUserRequestBody
}, function(error, response, body){
if (!error && response.statusCode === 200) {
const info = JSON.parse(body);
console.log("request successful");
console.log(response.statusCode);
console.log(info);
}
else{
console.log("request unsuccessful");
console.log(response.statusCode);
console.log(error);
}
});
return null;
});
Did you try with full path of end point to url: (including /v3/users)?
Or you may need to use "baseUrl" like below?
https://github.com/request/request#requestoptions-callback
Also, you need to make sure that you correctly used {application_id} value and {API Token} value.
You can double check this from your dashboard of SendBird.
http://dashboard.sendbird.com > Log in with your ID > select your APP.
There is a section named "App credentials" in "Overview" menu.
You can double check your API-request URL and API-Token value from there.

Editing Twilio TwiML using API or HTTP POST

My company uses Twilio Flex as our phone system and I was recently tasked with setting up a feature that will let us edit a TwiML voice message that plays before our normal voice message. This TwiML message will be changed through a Twilio bot that I've published in our Microsoft Teams.
The reason for this is so that our support desk can add a short message in the lines of "We're currently experiencing issues with X" before our normal "Welcome to [Company] support" message.
If TwiML's can be edited using HTTP POST/PUT or Twilio's API this should be a trivial matter, but so far I've not been able to figure out how.
I couldn't find any reference to this in the API doc, so I decided that HTTP POST would be the way to go. Using this as a start off point, I'm able to retrieve my TwiML using HTTP GET:
https://support.twilio.com/hc/en-us/articles/223132187--Not-Authorized-error-when-trying-to-view-TwiML-Bin-URL
const axios = require('axios');
const crypto = require('crypto');
const accountSidFlex = process.env.accountSidFlex;
const authTokenFlex = process.env.authTokenFlex;
var URL = 'https://handler.twilio.com/twiml/EHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + '?AccountSid=' + accountSidFlex
var twilioSig = crypto.createHmac('sha1', authTokenFlex).update(new Buffer(URL, 'utf-8')).digest('Base64')
var config = {
headers:{
'X-TWILIO-SIGNATURE': twilioSig
}
}
axios.get(
URL,config
).catch(error => console.log(error))
.then(response => {
console.log(response.data)
})
response.data shows the TwiML's current XML content.
My attempts at a POST only gives the same output as the GET, while PUT gives 405 Method Not Allowed.
var URL = 'https://handler.twilio.com/twiml/EHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + '?AccountSid=' + accountSidFlex
var twilioSig = crypto.createHmac('sha1', authTokenFlex).update(new Buffer(URL, 'utf-8')).digest('Base64')
var config = {
headers:{
'X-TWILIO-SIGNATURE': twilioSig,
'Content-Type': 'text/xml'
}
}
var xml =
'<?xml version="1.0" encoding="UTF-8"?>\
<Response><Play digits="www"/>\
<Say voice="alice">"We are currently experiencing X related issues". </Say>\
</Response>';
axios.post(
URL,xml,config
)
.catch(error => console.log(error))
.then(response => {
console.log(response.data)
})
Ideally I'd like to be able to change a specific TwiML using either HTTP methods or the Twilio-API, so that we can use it in out Studio Flow. We'd just keep it silent until we need to add something to it and revert back to silent once the issues have passed.
Any help would be appreciated!
You cannot currently change the contents of TwiML Bins, Studio Flows, or Twilio Functions programatically. I believe the key functionality you are looking for is a way to dynamically update the messaging (Say/Play Widget) in a Studio flow based on some condition.
One way is to use a Function Widget to retrieve a Twilio Sync document for the message, returning the message as JSON and have the Say/Play widget play that message. You can find the Twilio Sync REST API examples for Add, Modify, and Retrieve in the associated document.
You can retrieve the parsed response using variable syntax detailed here, https://www.twilio.com/docs/studio/widget-library#run-function.

Apostrophe CMS: Do we have middleware/hook which can set global.data from API?

My requiment is I have an API which will provide user data. In the Apostrophe CMS I need to access the user data from all the layouts (Header, Main, Footer).
I can see gobal.data which is avaiable everywhere in the template. Likewise I need a hook which will call the API and store the response data in the Apostrophe's global.data.
Please let me know if you need further informations.
You could hit that API on every page render:
// index.js of some apostrophe module
// You should `npm install request-promise` first
const request = require('request-promise');
module.exports = {
construct: function(self, options) {
self.on('apostrophe-pages:beforeSend', async function(req) {
const apiInfo = await request('http://some-api.com/something');
req.data.apiInfo = apiInfo;
// now in your templates you can access `data.apiInfo`
});
}
}
But this will hit that API on every single request which will of course make your site slow down. So I would recommend that you cache the information for some period of time.

How to integrate OAuth2.0 login in electron

I am newbie to electron and I am currently trying to implement an OAuth2.0 API which requires a callback URI. Url callback requires valid URL (https://myserver.com/sucess). so i tried this code snippet but does not work.
// Your GitHub Applications Credentials
var options = {
client_id: 'your_client_id',
client_secret: 'your_client_secret',
scopes: ["user:email", "notifications"] // Scopes limit access for OAuth tokens.
};
app.on('ready', () => {
// Build the OAuth consent page URL
var authWindow = new BrowserWindow({ width: 800, height: 600, show: false, 'node-integration': false });
var githubUrl = 'https://github.com/login/oauth/authorize?';
var authUrl = githubUrl + 'client_id=' + options.client_id + '&scope=' + options.scopes;
authWindow.loadURL(authUrl);
authWindow.show();
function handleCallback (url) {
console.log(url);
}
// Handle the response from GitHub - See Update from 4/12/2015
authWindow.webContents.on('will-navigate', function (event, url) {
handleCallback(url);
});
authWindow.webContents.on('did-get-redirect-request', function (event, oldUrl, newUrl) {
handleCallback(newUrl);
});
// Reset the authWindow on close
authWindow.on('close', function() {
authWindow = null;
}, false);
});
also, i used angular js route but does not work either.
so I'm wondering if there is a way to run server inside electron app to serve app from URL (https://localhost:3000) and if so how this will affect app behavior at packaging and distributing time, i means does the app will run from the same port
... any suggestions will help about how i can approach this problem. thank you
I had the same issue last week, i needed to integrate my electron app with vkontakte api which uses form of OAuth protocol. What you can do:
1) You launch local node http server, probably in separate process as i did.
2) You request code through oauth link and set redirect uri as http://127.0.0.1:8000/, for some reason https://localhost didn't work for me.
3) In main process you wait for message with code from server, on server implemented corresponding logic (when you receive request and code in it send through process.send back to parent message with code)
4)You request access token from main process, you shouldn't change redirect_uri. You again catch response from your server.
5) You get access_token, you kill server...
But when i did all this i read their docs till end and there was stated that standalone apps, like mine for desktop could receive token in easier way through "implicit flow", and you can get your token with only one call. Hope my experience could be extrapolated on your issue. Good luck!

Authentication for Node.js App with Angular.js and iOS Clients

I've tried to read as many different answers and posts as possible, but I still can't quite settle on a solution that fits my needs. I'm trying to work out the best (most efficient, but mostly more secure) way to handle user authentication, log in, etc.
I have a Node.js server, running on Express; I have an Angular.js web app; and I have an iOS app. I expose a RESTful API with Express/Node.js.
Cookies
The first things I read said to use cookies, and to store a session id/login token on the server side (hashed) and on the client side (unhashed). The client would transfer this id with each request, the server would hash it, parse it and process the request accordingly. This does not feel RESTful (not a huge issue), but more importantly, would I have to duplicate my API: one for username/password authentication (e.g. done via curl) and one for cookie-based authentication (e.g. my web app)?
Another problem with this: what I would do if I had multiple connections from the one user, e.g. they're logged in in two browsers, an iPhone and an iPad. Would my storage of their session ids need to now be an array?
HTTP Basic Auth
The next idea was to use HTTP Basic Auth (with SSL), which seems easy enough, but is not recommended because you need to transfer a username and password with each request. If I were to do it with HTTP Basic Auth, would I then store the username and password in cookies (or HTML local storage) to allow for 'Remember Me' functionality? Or could I combine the two: use HTTP Basic Auth for the actual requests (post a new post, etc.) and just use a session id stored in a cookie for the initial log in sequence/remember me aspects?
Is transmitting a session id more secure than just transmitting the user's password? How?
The session id is going to act ostensibly as a password, so to me transmitting it would have the same security issues as transmitting a password.
Basic Auth seems to be supported across all platforms, which is ideal. The main downside seems to be needing to transfer client authentication data with each request. Is there a way to mitigate this issue?
OAuth
OAuth seems like overkill for my needs. I think I would lose the ability to do curl commands to test my API. How is OAuth an improvement over the cookies method?
As you can probably tell, I'm a little confused by the diverse information available, so if you have a set of good links—applicable to this scenario—I would love to read them. I'm trying to find a solution that fits across all platforms, but is still as secure as possible. Also, if I have any of my terminology wrong, please correct me because it will make searching easier for me.
Thanks.
Update:
I've been thinking about this problem, and I've had an idea. Please tell me if this is dumb/insecure/any feedback, because I'm not sure if it's good.
When the user logs in, we generate a random session id (salted etc.). This optional session id is sent to the client, which the client can store (e.g. in cookies) if they choose; the session id is stored in the database.
This session id is then optionally sent with each request as either an HTTP Authentication header or query string, or the client can just send the username and password if they want (which gives us our regular REST API). At the server end, we check first for a session id parameter, if it's not present, we check for username/password. If neither are there—error.
On the server, we check that the session id is associated with the correct username. If it is, we complete the request.
Every time the user logs in, we create a new session id or delete the current one, and send this with the response to the log in request.
I think this lets me use the regular REST API, where appropriate, with Basic Auth, and maintain sessions/remember me functionality. It doesn't solve the multiple log ins issue, but otherwise I think this way should would. Please let me know.
I would use a token based authentication where you can send a token (automatically) with each request. You'll have to log in once, the server will provide you with a token which you can then use to send with each request. This token will be added to the HTML header, so that you don't have to modify each request to the browser.
You can set certain calls in the API so that they always need a token, while others might not be token protected.
For Express, you can use express-jwt (https://www.npmjs.org/package/express-jwt)
var expressJwt = require('express-jwt');
// Protect the /api routes with JWT
app.use('/api', expressJwt({secret: secret}));
app.use(express.json());
app.use(express.urlencoded());
If you want to authenticate you can create this function in your express server:
app.post('/authenticate', function (req, res) {
//if is invalid, return 401
if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) {
res.send(401, 'Wrong user or password');
return;
}
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// We are sending the profile inside the token
var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 });
res.json({ token: token });
});
And for protected calls something that starts with /api:
app.get('/api/restricted', function (req, res) {
console.log('user ' + req.user.email + ' is calling /api/restricted');
res.json({
name: 'foo'
});
});
In your Angular application you can login with:
$http
.post('/authenticate', $scope.user)
.success(function (data, status, headers, config) {
$window.sessionStorage.token = data.token;
$scope.message = 'Welcome';
})
.error(function (data, status, headers, config) {
// Erase the token if the user fails to log in
delete $window.sessionStorage.token;
// Handle login errors here
$scope.message = 'Error: Invalid user or password';
});
And by creating an authentication interceptor, it will automatically send the token with every request:
myApp.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
If you have to support old browsers which do not support local storage. You can swap the $window.sessionStorage with a library like AmplifyJS (http://amplifyjs.com/). Amplify for example uses whatever localstorage is available. This would translate in something like this:
if (data.status === 'OK') {
//Save the data using Amplify.js
localStorage.save('sessionToken', data.token);
//This doesn't work on the file protocol or on some older browsers
//$window.sessionStorage.token = data.token;
$location.path('/pep');
}
}).error(function (error) {
// Erase the token if the user fails to log in
localStorage.save('sessionToken', null);
// Handle login errors here
$scope.message = 'Error: Invalid user or password';
});
And the authintercepter we swap for:
angular.module('myApp.authInterceptor', ['myApp.localStorage']).factory('authInterceptor', [
'$rootScope',
'$q',
'localStorage',
function ($rootScope, $q, localStorage) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + localStorage.retrieve('sessionToken');
return config;
},
response: function (response) {
if (response.status === 401) {
}
return response || $q.when(response);
}
};
}
]);
You can find everything except AmplifyJS in this article:
http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Have a look to the yeoman generator for angular and node? The generator-angular-fullstack have a very nice structure for user authentification using passport.
You can see an example here :
the code: https://github.com/DaftMonk/fullstack-demo
the result: http://fullstack-demo.herokuapp.com/
Hope it helps!
I use generator-angular-fullstack, the /api services are not secured, get your _id from /api/users/me, logout, and go to /api/users/your_id_here, you will figure out that the /api not secured.

Resources