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
refreshflag 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
accessTokenUrlwith JSON body that will contain at leastclientId,clientSecret,codeandredirectUri(also possible to add extra fields viaextraAccessTokenParametersproperty). - Request to refresh token should be done via POST request to
accessTokenUrl(orrefreshTokenUrlif defined) with JSON body that will contain at leastclientId,clientSecretandrefreshToken(also possible to add extra fields viaextraRefreshTokenParametersproperty). - Both requests will return JSON response with body that contains
accessTokenand, 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';