Files
kami-parse-server/spec/Adapters/Auth/oauth2.spec.js

306 lines
8.8 KiB
JavaScript

const OAuth2Adapter = require('../../../lib/Adapters/Auth/oauth2').default;
describe('OAuth2Adapter', () => {
let adapter;
const validOptions = {
tokenIntrospectionEndpointUrl: 'https://provider.com/introspect',
useridField: 'sub',
appidField: 'aud',
appIds: ['valid-app-id'],
authorizationHeader: 'Bearer validAuthToken',
};
beforeEach(() => {
adapter = new OAuth2Adapter.constructor();
adapter.validateOptions(validOptions);
});
describe('validateAppId', () => {
it('should validate app ID successfully', async () => {
const authData = { access_token: 'validAccessToken' };
const mockResponse = {
[validOptions.appidField]: 'valid-app-id',
};
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
await expectAsync(
adapter.validateAppId(validOptions.appIds, authData, validOptions)
).toBeResolved();
});
it('should throw an error if app ID is invalid', async () => {
const authData = { access_token: 'validAccessToken' };
const mockResponse = {
[validOptions.appidField]: 'invalid-app-id',
};
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
await expectAsync(
adapter.validateAppId(validOptions.appIds, authData, validOptions)
).toBeRejectedWithError('OAuth2: Invalid app ID.');
});
});
describe('validateAuthData', () => {
it('should validate auth data successfully', async () => {
const authData = { id: 'user-id', access_token: 'validAccessToken' };
const mockResponse = {
active: true,
[validOptions.useridField]: 'user-id',
};
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
await expectAsync(
adapter.validateAuthData(authData, null, validOptions)
).toBeResolvedTo({});
});
it('should throw an error if the token is inactive', async () => {
const authData = { id: 'user-id', access_token: 'validAccessToken' };
const mockResponse = { active: false };
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
await expectAsync(
adapter.validateAuthData(authData, null, validOptions)
).toBeRejectedWith(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.'));
});
it('should throw an error if user ID does not match', async () => {
const authData = { id: 'user-id', access_token: 'validAccessToken' };
const mockResponse = {
active: true,
[validOptions.useridField]: 'different-user-id',
};
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
await expectAsync(
adapter.validateAuthData(authData, null, validOptions)
).toBeRejectedWithError('OAuth2 access token is invalid for this user.');
});
});
describe('requestTokenInfo', () => {
it('should fetch token info successfully', async () => {
const mockResponse = { active: true };
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve(mockResponse),
},
},
]);
const result = await adapter.requestTokenInfo(
'validAccessToken',
validOptions
);
expect(result).toEqual(mockResponse);
});
it('should throw an error if the introspection endpoint URL is missing', async () => {
const options = { ...validOptions, tokenIntrospectionEndpointUrl: null };
expect(
() => adapter.validateOptions(options)
).toThrow(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 token introspection endpoint URL is missing.'));
});
it('should throw an error if the response is not ok', async () => {
mockFetch([
{
url: validOptions.tokenIntrospectionEndpointUrl,
method: 'POST',
response: {
ok: false,
statusText: 'Bad Request',
},
},
]);
await expectAsync(
adapter.requestTokenInfo('invalidAccessToken')
).toBeRejectedWithError('OAuth2 token introspection request failed.');
});
});
describe('OAuth2Adapter E2E Tests', () => {
beforeEach(async () => {
// Simulate reconfiguring the server with OAuth2 auth options
await reconfigureServer({
auth: {
mockOauth: {
tokenIntrospectionEndpointUrl: 'https://provider.com/introspect',
useridField: 'sub',
appidField: 'aud',
appIds: ['valid-app-id'],
authorizationHeader: 'Bearer validAuthToken',
oauth2: true
},
},
});
});
it('should validate and authenticate user successfully', async () => {
mockFetch([
{
url: 'https://provider.com/introspect',
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve({
active: true,
sub: 'user123',
aud: 'valid-app-id',
}),
},
},
]);
const authData = { access_token: 'validAccessToken', id: 'user123' };
const user = await Parse.User.logInWith('mockOauth', { authData });
expect(user.id).toBeDefined();
expect(user.get('authData').mockOauth.id).toEqual('user123');
});
it('should reject authentication for inactive token', async () => {
mockFetch([
{
url: 'https://provider.com/introspect',
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve({ active: false, aud: ['valid-app-id'] }),
},
},
]);
const authData = { access_token: 'inactiveToken', id: 'user123' };
await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.')
);
});
it('should reject authentication for mismatched user ID', async () => {
mockFetch([
{
url: 'https://provider.com/introspect',
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve({
active: true,
sub: 'different-user',
aud: 'valid-app-id',
}),
},
},
]);
const authData = { access_token: 'validAccessToken', id: 'user123' };
await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.')
);
});
it('should reject authentication for invalid app ID', async () => {
mockFetch([
{
url: 'https://provider.com/introspect',
method: 'POST',
response: {
ok: true,
json: () => Promise.resolve({
active: true,
sub: 'user123',
aud: 'invalid-app-id',
}),
},
},
]);
const authData = { access_token: 'validAccessToken', id: 'user123' };
await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWithError(
'OAuth2: Invalid app ID.'
);
});
it('should handle error when token introspection endpoint is missing', async () => {
await reconfigureServer({
auth: {
mockOauth: {
tokenIntrospectionEndpointUrl: null,
useridField: 'sub',
appidField: 'aud',
appIds: ['valid-app-id'],
authorizationHeader: 'Bearer validAuthToken',
oauth2: true
},
},
});
const authData = { access_token: 'validAccessToken', id: 'user123' };
await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 token introspection endpoint URL is missing.')
);
});
});
});