mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-09 21:30:05 +00:00
feat: get access token & refresh tokens for server type SASJS
This commit is contained in:
36
src/auth/getAccessTokenForSasjs.ts
Normal file
36
src/auth/getAccessTokenForSasjs.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { prefixMessage } from '@sasjs/utils/error'
|
||||
import { RequestClient } from '../request/RequestClient'
|
||||
|
||||
/**
|
||||
* Exchanges the auth code for an access token for the given client.
|
||||
* @param requestClient - the pre-configured HTTP request client
|
||||
* @param clientId - the client ID to authenticate with.
|
||||
* @param authCode - the auth code received from the server.
|
||||
*/
|
||||
export async function getAccessTokenForSasjs(
|
||||
requestClient: RequestClient,
|
||||
clientId: string,
|
||||
authCode: string
|
||||
) {
|
||||
const url = '/SASjsApi/auth/token'
|
||||
const data = {
|
||||
clientId,
|
||||
code: authCode
|
||||
}
|
||||
|
||||
return await requestClient
|
||||
.post(url, data, undefined)
|
||||
.then((res) => {
|
||||
const sasAuth = res.result as {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
return {
|
||||
access_token: sasAuth.accessToken,
|
||||
refresh_token: sasAuth.refreshToken
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
throw prefixMessage(err, 'Error while getting access token. ')
|
||||
})
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { RequestClient } from '../request/RequestClient'
|
||||
* @param clientSecret - the client secret to authenticate with.
|
||||
* @param authCode - the auth code received from the server.
|
||||
*/
|
||||
export async function getAccessToken(
|
||||
export async function getAccessTokenForViya(
|
||||
requestClient: RequestClient,
|
||||
clientId: string,
|
||||
clientSecret: string,
|
||||
@@ -3,18 +3,21 @@ import {
|
||||
isRefreshTokenExpiring,
|
||||
hasTokenExpired
|
||||
} from '@sasjs/utils/auth'
|
||||
import { AuthConfig } from '@sasjs/utils/types'
|
||||
import { AuthConfig, ServerType } from '@sasjs/utils/types'
|
||||
import { RequestClient } from '../request/RequestClient'
|
||||
import { refreshTokens } from './refreshTokens'
|
||||
import { refreshTokensForViya } from './refreshTokensForViya'
|
||||
import { refreshTokensForSasjs } from './refreshTokensForSasjs'
|
||||
|
||||
/**
|
||||
* Returns the auth configuration, refreshing the tokens if necessary.
|
||||
* @param requestClient - the pre-configured HTTP request client
|
||||
* @param authConfig - an object containing a client ID, secret, access token and refresh token
|
||||
* @param serverType - server type for which refreshing the tokens, defaults to SASVIYA
|
||||
*/
|
||||
export async function getTokens(
|
||||
requestClient: RequestClient,
|
||||
authConfig: AuthConfig
|
||||
authConfig: AuthConfig,
|
||||
serverType: ServerType = ServerType.SasViya
|
||||
): Promise<AuthConfig> {
|
||||
const logger = process.logger || console
|
||||
let { access_token, refresh_token, client, secret } = authConfig
|
||||
@@ -29,12 +32,16 @@ export async function getTokens(
|
||||
throw new Error(error)
|
||||
}
|
||||
logger.info('Refreshing access and refresh tokens.')
|
||||
;({ access_token, refresh_token } = await refreshTokens(
|
||||
requestClient,
|
||||
client,
|
||||
secret,
|
||||
refresh_token
|
||||
))
|
||||
const tokens =
|
||||
serverType === ServerType.SasViya
|
||||
? await refreshTokensForViya(
|
||||
requestClient,
|
||||
client,
|
||||
secret,
|
||||
refresh_token
|
||||
)
|
||||
: await refreshTokensForSasjs(requestClient, refresh_token)
|
||||
;({ access_token, refresh_token } = tokens)
|
||||
}
|
||||
return { access_token, refresh_token, client, secret }
|
||||
}
|
||||
|
||||
35
src/auth/refreshTokensForSasjs.ts
Normal file
35
src/auth/refreshTokensForSasjs.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { prefixMessage } from '@sasjs/utils/error'
|
||||
import { RequestClient } from '../request/RequestClient'
|
||||
|
||||
/**
|
||||
* Exchanges the refresh token for an access token for the given client.
|
||||
* @param requestClient - the pre-configured HTTP request client
|
||||
* @param refreshToken - the refresh token received from the server.
|
||||
*/
|
||||
export async function refreshTokensForSasjs(
|
||||
requestClient: RequestClient,
|
||||
refreshToken: string
|
||||
) {
|
||||
const url = '/SASjsApi/auth/refresh'
|
||||
const headers = {
|
||||
Authorization: 'Bearer ' + refreshToken
|
||||
}
|
||||
|
||||
const authResponse = await requestClient
|
||||
.post(url, undefined, undefined, undefined, headers)
|
||||
.then((res) => {
|
||||
const sasAuth = res.result as {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
return {
|
||||
access_token: sasAuth.accessToken,
|
||||
refresh_token: sasAuth.refreshToken
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
throw prefixMessage(err, 'Error while refreshing tokens')
|
||||
})
|
||||
|
||||
return authResponse
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import { RequestClient } from '../request/RequestClient'
|
||||
* @param requestClient - the pre-configured HTTP request client
|
||||
* @param clientId - the client ID to authenticate with.
|
||||
* @param clientSecret - the client secret to authenticate with.
|
||||
* @param authCode - the refresh token received from the server.
|
||||
* @param refreshToken - the refresh token received from the server.
|
||||
*/
|
||||
export async function refreshTokens(
|
||||
export async function refreshTokensForViya(
|
||||
requestClient: RequestClient,
|
||||
clientId: string,
|
||||
clientSecret: string,
|
||||
65
src/auth/spec/getAccessTokenForSasjs.spec.ts
Normal file
65
src/auth/spec/getAccessTokenForSasjs.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { AuthConfig } from '@sasjs/utils'
|
||||
import { generateToken, mockSasjsAuthResponse } from './mockResponses'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import { getAccessTokenForSasjs } from '../getAccessTokenForSasjs'
|
||||
|
||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||
|
||||
describe('getAccessTokenForSasjs', () => {
|
||||
it('should attempt to refresh tokens', async () => {
|
||||
setupMocks()
|
||||
const access_token = generateToken(30)
|
||||
const refresh_token = generateToken(30)
|
||||
const authConfig: AuthConfig = {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client: 'cl13nt',
|
||||
secret: 's3cr3t'
|
||||
}
|
||||
jest
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({ result: mockSasjsAuthResponse, etag: '' })
|
||||
)
|
||||
|
||||
await getAccessTokenForSasjs(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.refresh_token
|
||||
)
|
||||
|
||||
expect(requestClient.post).toHaveBeenCalledWith(
|
||||
'/SASjsApi/auth/token',
|
||||
{ clientId: authConfig.client, code: authConfig.refresh_token },
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle errors while refreshing tokens', async () => {
|
||||
setupMocks()
|
||||
const access_token = generateToken(30)
|
||||
const refresh_token = generateToken(30)
|
||||
const authConfig: AuthConfig = {
|
||||
access_token,
|
||||
refresh_token,
|
||||
client: 'cl13nt',
|
||||
secret: 's3cr3t'
|
||||
}
|
||||
jest
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() => Promise.reject('Token Error'))
|
||||
|
||||
const error = await getAccessTokenForSasjs(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.refresh_token
|
||||
).catch((e) => e)
|
||||
|
||||
expect(error).toContain('Error while getting access token')
|
||||
})
|
||||
})
|
||||
|
||||
const setupMocks = () => {
|
||||
jest.restoreAllMocks()
|
||||
jest.mock('../../request/RequestClient')
|
||||
}
|
||||
@@ -2,11 +2,11 @@ import { AuthConfig } from '@sasjs/utils'
|
||||
import * as NodeFormData from 'form-data'
|
||||
import { generateToken, mockAuthResponse } from './mockResponses'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import { getAccessToken } from '../getAccessToken'
|
||||
import { getAccessTokenForViya } from '../getAccessTokenForViya'
|
||||
|
||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||
|
||||
describe('getAccessToken', () => {
|
||||
describe('getAccessTokenForViya', () => {
|
||||
it('should attempt to refresh tokens', async () => {
|
||||
setupMocks()
|
||||
const access_token = generateToken(30)
|
||||
@@ -26,7 +26,7 @@ describe('getAccessToken', () => {
|
||||
authConfig.client + ':' + authConfig.secret
|
||||
).toString('base64')
|
||||
|
||||
await getAccessToken(
|
||||
await getAccessTokenForViya(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
@@ -58,7 +58,7 @@ describe('getAccessToken', () => {
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() => Promise.reject('Token Error'))
|
||||
|
||||
const error = await getAccessToken(
|
||||
const error = await getAccessTokenForViya(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AuthConfig } from '@sasjs/utils'
|
||||
import * as refreshTokensModule from '../refreshTokens'
|
||||
import * as refreshTokensModule from '../refreshTokensForViya'
|
||||
import { generateToken, mockAuthResponse } from './mockResponses'
|
||||
import { getTokens } from '../getTokens'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
@@ -20,7 +20,7 @@ describe('getTokens', () => {
|
||||
|
||||
await getTokens(requestClient, authConfig)
|
||||
|
||||
expect(refreshTokensModule.refreshTokens).toHaveBeenCalledWith(
|
||||
expect(refreshTokensModule.refreshTokensForViya).toHaveBeenCalledWith(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
@@ -41,7 +41,7 @@ describe('getTokens', () => {
|
||||
|
||||
await getTokens(requestClient, authConfig)
|
||||
|
||||
expect(refreshTokensModule.refreshTokens).toHaveBeenCalledWith(
|
||||
expect(refreshTokensModule.refreshTokensForViya).toHaveBeenCalledWith(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
@@ -71,9 +71,9 @@ describe('getTokens', () => {
|
||||
const setupMocks = () => {
|
||||
jest.restoreAllMocks()
|
||||
jest.mock('../../request/RequestClient')
|
||||
jest.mock('../refreshTokens')
|
||||
jest.mock('../refreshTokensForViya')
|
||||
|
||||
jest
|
||||
.spyOn(refreshTokensModule, 'refreshTokens')
|
||||
.spyOn(refreshTokensModule, 'refreshTokensForViya')
|
||||
.mockImplementation(() => Promise.resolve(mockAuthResponse))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ export const mockAuthResponse: SasAuthResponse = {
|
||||
jti: 'test'
|
||||
}
|
||||
|
||||
export const mockSasjsAuthResponse = {
|
||||
access_token: 'acc355',
|
||||
refresh_token: 'r3fr35h'
|
||||
}
|
||||
|
||||
export const generateToken = (timeToLiveSeconds: number): string => {
|
||||
const exp =
|
||||
new Date(new Date().getTime() + timeToLiveSeconds * 1000).getTime() / 1000
|
||||
|
||||
47
src/auth/spec/refreshTokensForSasjs.spec.ts
Normal file
47
src/auth/spec/refreshTokensForSasjs.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { generateToken, mockAuthResponse } from './mockResponses'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import { refreshTokensForSasjs } from '../refreshTokensForSasjs'
|
||||
|
||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||
|
||||
describe('refreshTokensForSasjs', () => {
|
||||
it('should attempt to refresh tokens', async () => {
|
||||
setupMocks()
|
||||
const refresh_token = generateToken(30)
|
||||
jest
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({ result: mockAuthResponse, etag: '' })
|
||||
)
|
||||
|
||||
await refreshTokensForSasjs(requestClient, refresh_token)
|
||||
|
||||
expect(requestClient.post).toHaveBeenCalledWith(
|
||||
'/SASjsApi/auth/refresh',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{ Authorization: `Bearer ${refresh_token}` }
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle errors while refreshing tokens', async () => {
|
||||
setupMocks()
|
||||
const refresh_token = generateToken(30)
|
||||
jest
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() => Promise.reject('Token Error'))
|
||||
|
||||
const error = await refreshTokensForSasjs(
|
||||
requestClient,
|
||||
refresh_token
|
||||
).catch((e) => e)
|
||||
|
||||
expect(error).toContain('Error while refreshing tokens')
|
||||
})
|
||||
})
|
||||
|
||||
const setupMocks = () => {
|
||||
jest.restoreAllMocks()
|
||||
jest.mock('../../request/RequestClient')
|
||||
}
|
||||
@@ -2,11 +2,11 @@ import { AuthConfig } from '@sasjs/utils'
|
||||
import * as NodeFormData from 'form-data'
|
||||
import { generateToken, mockAuthResponse } from './mockResponses'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import { refreshTokens } from '../refreshTokens'
|
||||
import { refreshTokensForViya } from '../refreshTokensForViya'
|
||||
|
||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||
|
||||
describe('refreshTokens', () => {
|
||||
describe('refreshTokensForViya', () => {
|
||||
it('should attempt to refresh tokens', async () => {
|
||||
setupMocks()
|
||||
const access_token = generateToken(30)
|
||||
@@ -26,7 +26,7 @@ describe('refreshTokens', () => {
|
||||
authConfig.client + ':' + authConfig.secret
|
||||
).toString('base64')
|
||||
|
||||
await refreshTokens(
|
||||
await refreshTokensForViya(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
@@ -58,7 +58,7 @@ describe('refreshTokens', () => {
|
||||
.spyOn(requestClient, 'post')
|
||||
.mockImplementation(() => Promise.reject('Token Error'))
|
||||
|
||||
const error = await refreshTokens(
|
||||
const error = await refreshTokensForViya(
|
||||
requestClient,
|
||||
authConfig.client,
|
||||
authConfig.secret,
|
||||
Reference in New Issue
Block a user