diff --git a/guides/authentication.md b/guides/authentication.md index 2d1d301301..6a1b5a509c 100644 --- a/guides/authentication.md +++ b/guides/authentication.md @@ -20,6 +20,7 @@ To utilize authentication in browsers using the SDK you need to use the `client. - [Customize redirect URL](#customize-redirect-url-1) - [Advance](#advance) - [Manually trigger authentication](#manually-trigger-authentication) + - [Cache access tokens](#cache-access-tokens) - [Skip authentication](#skip-authentication) - [Combine different authentication methods](#combine-different-authentication-methods) - [Tokens](#tokens) @@ -157,6 +158,17 @@ client.loginWithOAuth({ await client.authenticate(); // this will also return a boolean based on if the user successfully authenticated or not. ``` +### Cache access tokens + +If you already have a valid access token you can use it to skip the authentication flow (see this [section](#tokens) on how to get hold of the token). +```js +client.loginWithOAuth({ + project: 'YOUR PROJECT NAME HERE', + accessToken: 'ACCESS TOKEN FOR THE PROJECT HERE', +}); +``` +> `client.authenticate()` will still override this and trigger a new authentication flow. + ### Skip authentication It is possible to skip the authentication like this: diff --git a/src/__tests__/cogniteClient.spec.ts b/src/__tests__/cogniteClient.spec.ts index 62fb2c2127..24fffded6b 100644 --- a/src/__tests__/cogniteClient.spec.ts +++ b/src/__tests__/cogniteClient.spec.ts @@ -333,6 +333,39 @@ describe('CogniteClient', () => { expect(promise401Throwed).toBe(true); await expect(promiseAuthenticate).resolves.toBe(false); }); + + describe('cached access token', () => { + test('should be able to provide an access token', async () => { + const client = setupClient(mockBaseUrl); + client.loginWithOAuth({ + project, + accessToken: authTokens.accessToken, + }); + nock(mockBaseUrl, { + reqheaders: { + [AUTHORIZATION_HEADER]: bearerString(authTokens.accessToken), + }, + }) + .get('/') + .once() + .reply(200, {}); + await client.get('/'); + }); + + test('re-authenticate on 401', async done => { + const client = setupClient(mockBaseUrl); + client.loginWithOAuth({ + project, + accessToken: authTokens.accessToken, + onAuthenticate: () => done(), + }); + nock(mockBaseUrl) + .get('/') + .once() + .reply(401, {}); + client.get('/'); + }); + }); }); }); }); diff --git a/src/__tests__/resources/groups.integration.spec.ts b/src/__tests__/resources/groups.integration.spec.ts index 47b2925689..d494f52c3d 100644 --- a/src/__tests__/resources/groups.integration.spec.ts +++ b/src/__tests__/resources/groups.integration.spec.ts @@ -73,7 +73,9 @@ describe('Groups integration test', () => { }); test('delete', async () => { - const response = await client.groups.delete([group.id]); - expect(response).toEqual({}); + await runTestWithRetryWhenFailing(async () => { + const response = await client.groups.delete([group.id]); + expect(response).toEqual({}); + }); }); }); diff --git a/src/cogniteClient.ts b/src/cogniteClient.ts index 24c223c5ef..f2c0957e38 100644 --- a/src/cogniteClient.ts +++ b/src/cogniteClient.ts @@ -61,6 +61,10 @@ export const POPUP = 'POPUP'; export interface OAuthLoginOptions extends Project { onAuthenticate?: OnAuthenticate | 'REDIRECT' | 'POPUP'; onTokens?: OnTokens; + /** + * Provide optional cached access token to skip the authentication flow (client.authenticate will still override this). + */ + accessToken?: string; } function validateAndReturnAPI(api: T | undefined): T { @@ -274,12 +278,13 @@ export default class CogniteClient { this.projectName = project; const onTokens = options.onTokens || (() => {}); - let onAuthenticate: OnAuthenticate = onAuthenticateWithRedirect; - if (options.onAuthenticate === POPUP) { - onAuthenticate = onAuthenticateWithPopup; - } else if (isFunction(options.onAuthenticate)) { - onAuthenticate = options.onAuthenticate; - } + const onAuthenticate = + options.onAuthenticate === POPUP + ? onAuthenticateWithPopup + : isFunction(options.onAuthenticate) + ? options.onAuthenticate + : onAuthenticateWithRedirect; + const authenticate = createAuthenticateFunction({ project, httpClient: this.httpClient, @@ -292,6 +297,11 @@ export default class CogniteClient { return didAuthenticate ? retry() : reject(); }); + const { accessToken } = options; + if (accessToken) { + this.httpClient.setBearerToken(accessToken); + } + this.initAPIs(); this.authenticate = authenticate; };