Skip to content

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:

index.js
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 least clientId, clientSecret, code and redirectUri (also possible to add extra fields via extraAccessTokenParameters property).
  • Request to refresh token should be done via POST request to accessTokenUrl (or refreshTokenUrl if defined) with JSON body that will contain at least clientId, clientSecret and refreshToken (also possible to add extra fields via extraRefreshTokenParameters property).
  • Both requests will return JSON response with body that contains accessToken and, if enabled, refreshToken (optional) and expireIn.

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

index.js
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

index.js
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

index.js
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:

index.js
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 framework
configuration.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.

index.js
configuration.projectIntegration.oauthLogin.mode = 'polling';