altair-graphql-core
Version:
Several of the core logic for altair graphql client
489 lines • 24.3 kB
JavaScript
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { OAuth2Client } from './client';
import { AuthFormat, OAuth2Type, RequestFormat } from './types';
import { setupServer } from 'msw/node';
import { MswMockRequestHandler } from '../test-helpers';
const server = setupServer();
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('oauth2 client', () => {
describe('getAuthorizationUrl', () => {
it('should return the authorization url', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
const url = await client.getAuthorizationUrl();
expect(url).toBe('https://auth-server.com/oauth/authorize?response_type=code&client_id=clientId&redirect_uri=redirectUri&state=random-state&scope=scope1+scope2&code_challenge=N1E4yRMD7xixn_oFyO_W3htYN3rY7-HMDKJe6z6r928&code_challenge_method=S256');
});
it('should throw an error if the flow is client credentials', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
await expect(client.getAuthorizationUrl()).rejects.toThrow('Client Credentials flow not supported for authorization code');
});
});
describe('getAuthorizationRedirectResponse', () => {
it('should return the authorization redirect response', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
location.href =
'https://auth-server.com/oauth/authorize?code=code&state=random-state';
const response = await client.getAuthorizationRedirectResponse();
expect(response).toEqual({
code: 'code',
state: 'random-state',
});
});
it('should return the authorization redirect error response', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
location.href =
'https://auth-server.com/oauth/authorize?error=access_denied&state=random-state&error_description=access+is+denied&error_uri=https%3A%2F%2Fauth-server.com%2Ferror';
const response = await client.getAuthorizationRedirectResponse();
expect(response).toEqual({
error: 'access_denied',
state: 'random-state',
error_description: 'access is denied',
error_uri: 'https://auth-server.com/error',
});
});
it('should return error if the state does not match', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
location.href =
'https://auth-server.com/oauth/authorize?code=code&state=random-state-2';
const response = await client.getAuthorizationRedirectResponse();
expect(response).toEqual({
error: 'invalid_state',
error_description: 'The state is invalid',
state: 'random-state-2',
});
});
it('should return undefined if the state is missing', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
location.href = 'https://auth-server.com/oauth/authorize?code=code';
const response = await client.getAuthorizationRedirectResponse();
expect(response).toBeUndefined();
});
it('should throw an error if the flow is client credentials', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
location.href =
'https://auth-server.com/oauth/authorize?code=code&state=random-state';
await expect(client.getAuthorizationRedirectResponse()).rejects.toThrow('Client Credentials flow not supported for authorization code');
});
});
describe('getAccessTokenFromCode', () => {
it('should return the access token from code (authFormat: in body, requestFormat: JSON)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromCode('code');
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
accept: 'application/json',
'content-type': 'application/json',
});
expect(await receivedRequest?.json()).toEqual({
grant_type: 'authorization_code',
code: 'code',
redirect_uri: 'redirectUri',
client_id: 'clientId',
client_secret: 'clientSecret',
code_verifier: 'codeVerifier',
});
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from code (authFormat: basic auth, requestFormat: JSON)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.BASIC_AUTH,
requestFormat: RequestFormat.JSON,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromCode('code');
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
accept: 'application/json',
'content-type': 'application/json',
authorization: 'Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0',
});
expect(await receivedRequest?.json()).toEqual({
grant_type: 'authorization_code',
code: 'code',
redirect_uri: 'redirectUri',
code_verifier: 'codeVerifier',
});
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from code (authFormat: in body, requestFormat: form)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.FORM,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromCode('code');
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
'content-type': 'application/x-www-form-urlencoded',
});
expect(await receivedRequest?.text()).toEqual('grant_type=authorization_code&code=code&redirect_uri=redirectUri&client_id=clientId&client_secret=clientSecret&code_verifier=codeVerifier');
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from code (authFormat: basic auth, requestFormat: form)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.BASIC_AUTH,
requestFormat: RequestFormat.FORM,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromCode('code');
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
'content-type': 'application/x-www-form-urlencoded',
authorization: 'Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0',
});
expect(await receivedRequest?.text()).toEqual('grant_type=authorization_code&code=code&redirect_uri=redirectUri&code_verifier=codeVerifier');
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should throw an error if the flow is client credentials', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
await expect(client.getAccessTokenFromCode('code')).rejects.toThrow('Client Credentials flow not supported');
});
});
describe('getAccessTokenFromClientCredentials', () => {
it('should return the access token from client credentials (authFormat: in body, requestFormat: JSON)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.JSON,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromClientCredentials();
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
accept: 'application/json',
'content-type': 'application/json',
});
expect(await receivedRequest?.json()).toEqual({
grant_type: 'client_credentials',
client_id: 'clientId',
client_secret: 'clientSecret',
scope: 'scope1 scope2',
});
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from client credentials (authFormat: basic auth, requestFormat: JSON)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.BASIC_AUTH,
requestFormat: RequestFormat.JSON,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromClientCredentials();
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
accept: 'application/json',
'content-type': 'application/json',
authorization: 'Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0',
});
expect(await receivedRequest?.json()).toEqual({
grant_type: 'client_credentials',
scope: 'scope1 scope2',
});
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from client credentials (authFormat: in body, requestFormat: form)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.FORM,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromClientCredentials();
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
'content-type': 'application/x-www-form-urlencoded',
});
expect(await receivedRequest?.text()).toEqual('grant_type=client_credentials&client_id=clientId&client_secret=clientSecret&scope=scope1+scope2');
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should return the access token from client credentials (authFormat: basic auth, requestFormat: form)', async () => {
const client = new OAuth2Client({
type: OAuth2Type.CLIENT_CREDENTIALS,
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
scopes: ['scope1', 'scope2'],
authFormat: AuthFormat.BASIC_AUTH,
requestFormat: RequestFormat.FORM,
});
const mockHandler = new MswMockRequestHandler('https://auth-server.com/oauth/token', async () => {
return Response.json({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
server.use(mockHandler);
const response = await client.getAccessTokenFromClientCredentials();
const receivedRequest = mockHandler.receivedRequest();
expect(receivedRequest?.method).toBe('POST');
expect(Object.fromEntries(receivedRequest?.headers)).toEqual({
'content-type': 'application/x-www-form-urlencoded',
authorization: 'Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0',
});
expect(await receivedRequest?.text()).toEqual('grant_type=client_credentials&scope=scope1+scope2');
expect(response).toEqual({
access_token: 'access-token',
refresh_token: 'refresh-token',
expires_in: 3600,
token_type: 'Bearer',
});
});
it('should throw an error if the flow is not client credentials', async () => {
const client = new OAuth2Client({
type: OAuth2Type.AUTHORIZATION_CODE_PKCE,
authorizationEndpoint: 'https://auth-server.com/oauth/authorize',
tokenEndpoint: 'https://auth-server.com/oauth/token',
clientId: 'clientId',
clientSecret: 'clientSecret',
redirectUri: 'redirectUri',
scopes: ['scope1', 'scope2'],
codeVerifier: 'codeVerifier',
state: 'random-state',
authFormat: AuthFormat.IN_BODY,
requestFormat: RequestFormat.FORM,
});
await expect(client.getAccessTokenFromClientCredentials()).rejects.toThrow('Only Client Credentials flow is supported');
});
});
// getAuthorizationRedirectResponse
// getAccessTokenFromCode
// getAccessTokenFromClientCredentials
});
//# sourceMappingURL=client.spec.js.map