Skip to content

Commit

Permalink
feat(medusa-plugin-auth): added oauth2 as login strategy (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephane-segning authored Nov 28, 2023
1 parent e95fe7b commit 4dde834
Show file tree
Hide file tree
Showing 11 changed files with 770 additions and 7 deletions.
4 changes: 3 additions & 1 deletion packages/medusa-plugin-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@medusajs/medusa": ">=1.17.x",
"@types/express": "^4.17.17",
"@types/jest": "^29.1.2",
"@types/passport-oauth2": "^1.4.15",
"jest": "^29.1.2",
"passport": "^0.6.0",
"ts-jest": "^29.0.3",
Expand All @@ -72,7 +73,8 @@
"passport-facebook": "^3.0.0",
"passport-firebase-jwt": "^1.2.1",
"passport-google-oauth2": "^0.2.0",
"passport-linkedin-oauth2": "^2.0.0"
"passport-linkedin-oauth2": "^2.0.0",
"passport-oauth2": "^1.7.0"
},
"jest": {
"preset": "ts-jest",
Expand Down
2 changes: 2 additions & 0 deletions packages/medusa-plugin-auth/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Router } from 'express';
import { ConfigModule } from '@medusajs/medusa/dist/types/global';
import loadConfig from '@medusajs/medusa/dist/loaders/config';
import OAuth2Strategy from '../auth-strategies/oauth2';
import GoogleStrategy from '../auth-strategies/google';
import FacebookStrategy from '../auth-strategies/facebook';
import LinkedinStrategy from '../auth-strategies/linkedin';
Expand All @@ -18,6 +19,7 @@ export default function (rootDirectory, pluginOptions: AuthOptions): Router[] {
function loadRouters(configModule: ConfigModule, options: AuthOptions): Router[] {
const routers: Router[] = [];

routers.push(...OAuth2Strategy.getRouter(configModule, options));
routers.push(...GoogleStrategy.getRouter(configModule, options));
routers.push(...FacebookStrategy.getRouter(configModule, options));
routers.push(...LinkedinStrategy.getRouter(configModule, options));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { ConfigModule, MedusaContainer } from '@medusajs/medusa/dist/types/global';
import { OAuth2AdminStrategy } from '../../admin';
import { AUTH_PROVIDER_KEY } from '../../../../types';
import { OAUTH2_ADMIN_STRATEGY_NAME, OAuth2AuthOptions } from '../../types';

describe('OAuth2 admin strategy verify callback', function() {
const existsEmail = '[email protected]';
const existsEmailWithProviderKey = '[email protected]';
const existsEmailWithWrongProviderKey = '[email protected]';

let container: MedusaContainer;
let req: Request;
let accessToken: string;
let refreshToken: string;
let profile: { emails: { value: string }[]; name?: { givenName?: string; familyName?: string } };
let oauth2AdminStrategy: OAuth2AdminStrategy;

beforeEach(() => {
profile = {
emails: [{ value: existsEmail }],
};

container = {
resolve: (name: string) => {
const container_ = {
userService: {
retrieveByEmail: jest.fn().mockImplementation(async (email: string) => {
if (email === existsEmail) {
return {
id: 'test',
};
}

if (email === existsEmailWithProviderKey) {
return {
id: 'test2',
metadata: {
[AUTH_PROVIDER_KEY]: OAUTH2_ADMIN_STRATEGY_NAME,
},
};
}

if (email === existsEmailWithWrongProviderKey) {
return {
id: 'test3',
metadata: {
[AUTH_PROVIDER_KEY]: 'fake_provider_key',
},
};
}

return;
}),
},
};

return container_[name];
},
} as MedusaContainer;
});

describe('when strict is set to admin', function() {
beforeEach(() => {
oauth2AdminStrategy = new OAuth2AdminStrategy(
container,
{} as ConfigModule,
{
authorizationURL: 'http://localhost',
tokenURL: 'http://localhost',
clientID: 'fake',
clientSecret: 'fake',
admin: {},
} as OAuth2AuthOptions,
'admin',
);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should succeed', async () => {
profile = {
emails: [{ value: existsEmailWithProviderKey }],
};

const data = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile);
expect(data).toEqual(
expect.objectContaining({
id: 'test2',
}),
);
});

it('should fail when a user exists without the auth provider metadata', async () => {
profile = {
emails: [{ value: existsEmail }],
};

const err = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile).catch((err) => err);
expect(err).toEqual(new Error(`Admin with email ${existsEmail} already exists`));
});

it('should fail when a user exists with the wrong auth provider key', async () => {
profile = {
emails: [{ value: existsEmailWithWrongProviderKey }],
};

const err = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile).catch((err) => err);
expect(err).toEqual(new Error(`Admin with email ${existsEmailWithWrongProviderKey} already exists`));
});

it('should fail when the user does not exist', async () => {
profile = {
emails: [{ value: 'fake' }],
};

const err = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile).catch((err) => err);
expect(err).toEqual(new Error(`Unable to authenticate the user with the email fake`));
});
});

describe('when strict is set for store only', function() {
beforeEach(() => {
oauth2AdminStrategy = new OAuth2AdminStrategy(
container,
{} as ConfigModule,
{
authorizationURL: 'http://localhost',
tokenURL: 'http://localhost',
clientID: 'fake',
clientSecret: 'fake',
admin: {},
} as OAuth2AuthOptions,
'store',
);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should succeed', async () => {
profile = {
emails: [{ value: existsEmailWithProviderKey }],
};

const data = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile);
expect(data).toEqual(
expect.objectContaining({
id: 'test2',
}),
);
});

it('should succeed when a user exists without the auth provider metadata', async () => {
profile = {
emails: [{ value: existsEmail }],
};

const data = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile);
expect(data).toEqual(
expect.objectContaining({
id: 'test',
}),
);
});

it('should succeed when a user exists with the wrong auth provider key', async () => {
profile = {
emails: [{ value: existsEmailWithWrongProviderKey }],
};

const data = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile);
expect(data).toEqual(
expect.objectContaining({
id: 'test3',
}),
);
});

it('should fail when the user does not exist', async () => {
profile = {
emails: [{ value: 'fake' }],
};

const err = await oauth2AdminStrategy.validate(req, accessToken, refreshToken, profile).catch((err) => err);
expect(err).toEqual(new Error(`Unable to authenticate the user with the email fake`));
});
});
});
Loading

1 comment on commit 4dde834

@vercel
Copy link

@vercel vercel bot commented on 4dde834 Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

medusa-plugins – ./

medusa-plugins.vercel.app
medusa-plugins-git-main-adrien2p.vercel.app
medusa-plugins-adrien2p.vercel.app

Please sign in to comment.