Authorization
Most of the time, you will need to authenticate your app with a third-party service. This can be done in a few different ways, depending on the service you are integrating with.
Customize The Login Form
By default, the login page for your application will only require you to enter the apiToken
to communicate with the third party service. There is a way to customize this:
configuration.projectIntegration.loginForm = { fields: [ // Fields Reference ], performGetTokenRequest: async (fieldsCredentials) => { const getTokenUrl = `${authUrl}/oauth/token?grant_type=client_credentials`; const tokenId = base64.encode(`${fieldsCredentials.username}:${fieldsCredentials.password}`); const headers = {Authorization: `Bearer ${tokenId}`};
const clientCredentials = await axios.get(getTokenUrl, { headers });
return { access_token: clientCredentials.data.access_token, refresh_token: clientCredentials.data.refresh_token, expires_in: clientCredentials.data.expires_in // seconds }; }, refresh: true, performRefreshTokenRequest: async (credentials) => { const getTokenUrl = `${authUrl}/oauth/token?refresh=true&grant_type=client_credentials`; const headers = {'Authorization': `Bearer ${credentials.refreshToken}`};
const clientCredentials = await axios.get(getTokenUrl, { headers });
return { access_token: clientCredentials.data.access_token, refresh_token: clientCredentials.data.refresh_token, expires_in: clientCredentials.data.expires_in // seconds }; }};
Fields Reference
Label
{ label: 'API Credentials'}
Input
{ key: 'username', label: 'Username'}
Additional properties:
type: 'password' | 'textarea'
- Input type.helpText: string
- Help text for the field.
Select
{ key: 'server', label: 'Data center', type: 'select', defaultValue: '1', options: [ { value: '1', label: 'USA' }, { value: '2', label: 'EU' } ]}
File
{ key: 'fileSecret', label: 'Upload file', type: 'file', accept: '.txt'}
performGetTokenRequest
Function
This function is called when the user submits the login form. It should return an object with the following properties: access_token
, refresh_token
, and expires_in
. The expires_in
property should be in seconds.
performRefreshTokenRequest
Function
If the access token has an expiration date and needs to be refreshed, set refresh: true
and implement the performRefreshTokenRequest
function.
This function should return an object with the following properties: access_token
, refresh_token
, and expires_in
. The expires_in
property should be in seconds.
OAuth2 Support
If the third-party service uses OAuth2 for authorization, use the oauthLogin
field to configure it.
The oauthLogin
property allows you to customize many different properties, mappings, etc. So that you can integrate with any OAuth2 implementation.
Main default values:
- Redirect uri prefix:
/oauth/code
. - Client ID field name in url parameters and in request payload:
client_id
. - Client Secret field name in request payload:
client_secret
. - State field name in url parameters:
state
. - Access token field name:
access_token
. - The default assumption is that tokens do not have an expiration date, to change this behavior use the
refresh
flag to refresh the token and take expiration into account. - Refresh token field name:
refresh_token
. - Expires in field name:
expires_in
(value should be in seconds).
This module relies on the OAuth2 protocol being implemented by a third-party service in this way:
- Request for access token should be done via POST request to
accessTokenUrl
with JSON body that will contain at leastclientId
,clientSecret
,code
andredirectUri
(also possible to add extra fields viaextraAccessTokenParameters
property). - Request to refresh token should be done via POST request to
accessTokenUrl
(orrefreshTokenUrl
if defined) with JSON body that will contain at leastclientId
,clientSecret
andrefreshToken
(also possible to add extra fields viaextraRefreshTokenParameters
property). - Both requests will return JSON response with body that contains
accessToken
and, if enabled,refreshToken
(optional) andexpireIn
.
To override these requests, please use performGetTokenRequest
and performRefreshTokenRequest
(e.g. if requests should be made with different HTTP methods or data should be passed as query string or form data).
GitHub Example
configuration.projectIntegration.oauthLogin = { authorizationUrl: 'https://github.com/login/oauth/authorize', clientId: 'github_app_client_id', clientSecret: 'github_app_client_secret', accessTokenUrl: 'https://github.com/login/oauth/access_token'}
Google Example
configuration.projectIntegration.oauthLogin = { scope: 'https%3A//www.googleapis.com/auth/userinfo.email', authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth', clientId: 'google_web_app_client_id', clientSecret: 'google_web_app_client_secret', accessTokenUrl: 'https://oauth2.googleapis.com/token', extraAutorizationUrlParameters: { response_type: 'code', access_type: 'offline', prompt: 'consent' }, extraAccessTokenParameters: { grant_type: 'authorization_code' }, extraRefreshTokenParameters: { grant_type: 'refresh_token' }, refresh: true}
Mailup Example
const clientId = 'client_id';const clientSecret = 'client_secret';const tokenUrl = 'https://services.mailup.com/Authorization/OAuth/Token';
configuration.projectIntegration.oauthLogin = { authorizationUrl: 'https://services.mailup.com/Authorization/OAuth/LogOn', clientId, clientSecret, extraAutorizationUrlParameters: { response_type: 'code' }, refresh: true, performGetTokenRequest: async (code, query, url, redirectUri, loginForm) => { //query is an object with all query params //url is an url string that OAuth server used to call us back const getTokenUrl = `${tokenUrl}?code=${code}&grant_type=authorization_code`; const headers = { 'Authorization': `Bearer ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}` }; return (await axios.get(getTokenUrl, { headers })).data; }, performRefreshTokenRequest: async (credentials, loginForm) => { const params = { refresh_token: credentials.refreshToken, grant_type: 'refresh_token', client_id: clientId, client_secret: clientSecret }; const data = Object.keys(params) .map((key) => `${key}=${encodeURIComponent(params[key])}`) .join('&'); const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; return (await axios.post(tokenUrl, data, { headers })).data; }}
Customization
In addition, you can specify extra fields on login screen that you can use to dynamically build authorization url:
configuration.projectIntegration.oauthLogin.loginFields = [ { key: 'region', label: 'Region', type: 'select', defaultValue: 'NA', options: [ { value: 'NA', label: 'North American' }, { value: 'EU', label: 'Europe' }, { value: 'AZURE_NA', label: 'Azure North American' } ] }];
//NOTE: url should not append state! `state` is utilized by frameworkconfiguration.projectIntegration.oauthLogin.getAuthorizationUrl = (redirectUrl, loginForm) => { const region = loginForm.region; if (region === 'EU') { return `https://eu-region.com?client_id=<client-id>&redirect_uri=${redirectUrl}`; } else { return `https://default-region.com?client_id=<client-id>&redirect_uri=${redirectUrl}`; }};
By default, OAuth works in a way that app opens popup, where user performs login, and on successful action this popup will be closed and main window will receive an event and will finalize the login flow. But in some rare cases there might be an issue of sending this event to main window. In this case please use polling
mode.
configuration.projectIntegration.oauthLogin.mode = 'polling';