Skip to content

Commit

Permalink
Features: #123 Array like options (#124)
Browse files Browse the repository at this point in the history
* feat(auth): added new array-like structure

* bump: simplify the array extraction

Co-authored-by: Adrien de Peretti <[email protected]>

* bump(auth): lint fix

* fix(auth): iteration over auth options

---------

Co-authored-by: Adrien de Peretti <[email protected]>
  • Loading branch information
stephane-segning and adrien2p authored Dec 6, 2023
1 parent 6f0a633 commit 65e7439
Show file tree
Hide file tree
Showing 49 changed files with 1,164 additions and 899 deletions.
5 changes: 5 additions & 0 deletions packages/medusa-plugin-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
"@medusajs/medusa": ">=1.17.x",
"@types/express": "^4.17.17",
"@types/jest": "^29.1.2",
"@types/passport-auth0": "^1.0.9",
"@types/passport-azure-ad": "^4.3.5",
"@types/passport-facebook": "^3.0.3",
"@types/passport-google-oauth2": "^0.1.8",
"@types/passport-linkedin-oauth2": "^1.5.6",
"@types/passport-oauth2": "^1.4.15",
"jest": "^29.1.2",
"passport": "^0.6.0",
Expand Down
42 changes: 31 additions & 11 deletions packages/medusa-plugin-auth/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,43 @@ import FirebaseStrategy from '../auth-strategies/firebase';
import Auth0Strategy from '../auth-strategies/auth0';
import AzureStrategy from '../auth-strategies/azure-oidc';

import { AuthOptions } from '../types';
import { AuthOptions, AuthOptionsWrapper, handleOption } from '../types';

export default function (rootDirectory, pluginOptions: AuthOptions): Router[] {
const configModule = loadConfig(rootDirectory) as ConfigModule;
export default async function (rootDirectory, pluginOptions: AuthOptions[]): Promise<Router[]> {
const configModule = loadConfig(rootDirectory);
return loadRouters(configModule, pluginOptions);
}

function loadRouters(configModule: ConfigModule, options: AuthOptions): Router[] {
async function loadRouters(configModule: ConfigModule, options: AuthOptionsWrapper[]): Promise<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));
routers.push(...FirebaseStrategy.getRouter(configModule, options));
routers.push(...Auth0Strategy.getRouter(configModule, options));
routers.push(...AzureStrategy.getRouter(configModule, options));
for (const opt of options) {
const option = await handleOption(opt, configModule);

switch (option.type) {
case 'azure_oidc':
routers.push(...AzureStrategy.getRouter(configModule, option));
break;
case 'google':
routers.push(...GoogleStrategy.getRouter(configModule, option));
break;
case 'facebook':
routers.push(...FacebookStrategy.getRouter(configModule, option));
break;
case 'linkedin':
routers.push(...LinkedinStrategy.getRouter(configModule, option));
break;
case 'firebase':
routers.push(...FirebaseStrategy.getRouter(configModule, option));
break;
case 'auth0':
routers.push(...Auth0Strategy.getRouter(configModule, option));
break;
case 'oauth2':
routers.push(...OAuth2Strategy.getRouter(configModule, option));
break;
}
}

return routers;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ConfigModule, MedusaContainer } from '@medusajs/medusa/dist/types/global';
import { Auth0AdminStrategy } from '../../admin';
import { AUTH_PROVIDER_KEY } from '../../../../types';
import { Auth0Options, AUTH0_ADMIN_STRATEGY_NAME, Profile, ExtraParams } from '../../types';
import { AUTH_PROVIDER_KEY, IStrategy } from '../../../../types';
import { AUTH0_ADMIN_STRATEGY_NAME, Auth0Options, ExtraParams } from '../../types';
import { Profile } from 'passport-auth0';
import { getAuth0AdminStrategy } from '../../admin';

describe('Auth0 admin strategy verify callback', function () {
const existsEmail = '[email protected]';
Expand All @@ -12,9 +13,9 @@ describe('Auth0 admin strategy verify callback', function () {
let req: Request;
let accessToken: string;
let refreshToken: string;
let profile: Profile;
let profile: Partial<Profile>;
let extraParams: ExtraParams;
let auth0AdminStrategy: Auth0AdminStrategy;
let auth0AdminStrategy: IStrategy;

beforeEach(() => {
profile = {
Expand All @@ -38,7 +39,7 @@ describe('Auth0 admin strategy verify callback', function () {
return {
id: 'test2',
metadata: {
[AUTH_PROVIDER_KEY]: AUTH0_ADMIN_STRATEGY_NAME,
[AUTH_PROVIDER_KEY]: AUTH0_ADMIN_STRATEGY_NAME + '_test',
},
};
}
Expand All @@ -64,6 +65,7 @@ describe('Auth0 admin strategy verify callback', function () {

describe('when strict is set to admin', function () {
beforeEach(() => {
const Auth0AdminStrategy = getAuth0AdminStrategy('test');
auth0AdminStrategy = new Auth0AdminStrategy(
container,
{} as ConfigModule,
Expand Down Expand Up @@ -130,6 +132,7 @@ describe('Auth0 admin strategy verify callback', function () {

describe('when strict is set for store only', function () {
beforeEach(() => {
const Auth0AdminStrategy = getAuth0AdminStrategy('test');
auth0AdminStrategy = new Auth0AdminStrategy(
container,
{} as ConfigModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Auth0StoreStrategy } from '../../store';
import { ConfigModule, MedusaContainer } from '@medusajs/medusa/dist/types/global';
import { AUTH_PROVIDER_KEY, CUSTOMER_METADATA_KEY } from '../../../../types';
import { Auth0Options, AUTH0_STORE_STRATEGY_NAME, Profile, ExtraParams } from '../../types';
import { AUTH_PROVIDER_KEY, CUSTOMER_METADATA_KEY, IStrategy } from '../../../../types';
import { AUTH0_STORE_STRATEGY_NAME, Auth0Options, ExtraParams } from '../../types';
import { Profile } from 'passport-auth0';
import { getAuth0StoreStrategy } from '../../store';

describe('Auth0 store strategy verify callback', function () {
const existsEmail = '[email protected]';
Expand All @@ -13,9 +14,9 @@ describe('Auth0 store strategy verify callback', function () {
let req: Request;
let accessToken: string;
let refreshToken: string;
let profile: Profile;
let profile: Partial<Profile>;
let extraParams: ExtraParams;
let auth0StoreStrategy: Auth0StoreStrategy;
let auth0StoreStrategy: IStrategy;
let updateFn;
let createFn;

Expand Down Expand Up @@ -67,7 +68,7 @@ describe('Auth0 store strategy verify callback', function () {
id: 'test3',
metadata: {
[CUSTOMER_METADATA_KEY]: true,
[AUTH_PROVIDER_KEY]: AUTH0_STORE_STRATEGY_NAME,
[AUTH_PROVIDER_KEY]: AUTH0_STORE_STRATEGY_NAME + '_test',
},
};
}
Expand All @@ -94,6 +95,7 @@ describe('Auth0 store strategy verify callback', function () {

describe('when strict is set to store', function () {
beforeEach(() => {
const Auth0StoreStrategy = getAuth0StoreStrategy('test');
auth0StoreStrategy = new Auth0StoreStrategy(
container,
{} as ConfigModule,
Expand Down Expand Up @@ -183,6 +185,7 @@ describe('Auth0 store strategy verify callback', function () {

describe('when strict is set to admin', function () {
beforeEach(() => {
const Auth0StoreStrategy = getAuth0StoreStrategy('test');
auth0StoreStrategy = new Auth0StoreStrategy(
container,
{} as ConfigModule,
Expand Down
102 changes: 54 additions & 48 deletions packages/medusa-plugin-auth/src/auth-strategies/auth0/admin.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,83 @@
import { Strategy as Auth0Strategy } from 'passport-auth0';
import { Profile, Strategy as Auth0Strategy, StrategyOptionWithRequest } from 'passport-auth0';
import { ConfigModule, MedusaContainer } from '@medusajs/medusa/dist/types/global';
import { Router } from 'express';
import { AUTH0_ADMIN_STRATEGY_NAME, Auth0Options, Profile, ExtraParams } from './types';
import { AUTH0_ADMIN_STRATEGY_NAME, Auth0Options, ExtraParams } from './types';
import { PassportStrategy } from '../../core/passport/Strategy';
import { validateAdminCallback } from '../../core/validate-callback';
import { passportAuthRoutesBuilder } from '../../core/passport/utils/auth-routes-builder';
import { AuthOptions } from '../../types';
import { AuthProvider, StrategyFactory } from '../../types';

export class Auth0AdminStrategy extends PassportStrategy(Auth0Strategy, AUTH0_ADMIN_STRATEGY_NAME) {
constructor(
protected readonly container: MedusaContainer,
protected readonly configModule: ConfigModule,
protected readonly strategyOptions: Auth0Options,
protected readonly strict?: AuthOptions['strict']
) {
super({
domain: strategyOptions.auth0Domain,
clientID: strategyOptions.clientID,
clientSecret: strategyOptions.clientSecret,
callbackURL: strategyOptions.admin.callbackUrl,
passReqToCallback: true,
state: true,
});
}
export function getAuth0AdminStrategy(id: string): StrategyFactory<Auth0Options> {
const strategyName = `${AUTH0_ADMIN_STRATEGY_NAME}_${id}`;
return class extends PassportStrategy(Auth0Strategy, strategyName) {
constructor(
protected readonly container: MedusaContainer,
protected readonly configModule: ConfigModule,
protected readonly strategyOptions: Auth0Options,
protected readonly strict?: AuthProvider['strict']
) {
super({
domain: strategyOptions.auth0Domain,
clientID: strategyOptions.clientID,
clientSecret: strategyOptions.clientSecret,
callbackURL: strategyOptions.admin.callbackUrl,
passReqToCallback: true,
state: true,
} as StrategyOptionWithRequest);
}

async validate(
req: Request,
accessToken: string,
refreshToken: string,
extraParams: ExtraParams,
profile: Profile
): Promise<null | { id: string; accessToken: string }> {
if (this.strategyOptions.admin.verifyCallback) {
const validateRes = await this.strategyOptions.admin.verifyCallback(
this.container,
req,
accessToken,
refreshToken,
extraParams,
profile,
this.strict
);
async validate(
req: Request,
accessToken: string,
refreshToken: string,
extraParams: ExtraParams,
profile: Profile
): Promise<null | { id: string; accessToken: string }> {
if (this.strategyOptions.admin.verifyCallback) {
const validateRes = await this.strategyOptions.admin.verifyCallback(
this.container,
req,
accessToken,
refreshToken,
extraParams,
profile,
this.strict
);

return {
...validateRes,
accessToken,
};
}
const validateRes = await validateAdminCallback(profile, {
container: this.container,
strategyErrorIdentifier: 'auth0',
strict: this.strict,
strategyName,
});
return {
...validateRes,
accessToken,
};
}
const validateRes = await validateAdminCallback(profile, {
container: this.container,
strategyErrorIdentifier: 'auth0',
strict: this.strict,
});
return {
...validateRes,
accessToken,
};
}
};
}

/**
* Return the router that holds the auth0 admin authentication routes
* @param id
* @param auth0
* @param configModule
*/
export function getAuth0AdminAuthRouter(auth0: Auth0Options, configModule: ConfigModule): Router {
export function getAuth0AdminAuthRouter(id: string, auth0: Auth0Options, configModule: ConfigModule): Router {
const strategyName = `${AUTH0_ADMIN_STRATEGY_NAME}_${id}`;
return passportAuthRoutesBuilder({
domain: 'admin',
configModule,
authPath: auth0.admin.authPath ?? '/admin/auth/auth0',
authCallbackPath: auth0.admin.authCallbackPath ?? '/admin/auth/auth0/cb',
successRedirect: auth0.admin.successRedirect,
strategyName: AUTH0_ADMIN_STRATEGY_NAME,
strategyName,
passportAuthenticateMiddlewareOptions: {
scope: 'openid email profile',
},
Expand Down
34 changes: 19 additions & 15 deletions packages/medusa-plugin-auth/src/auth-strategies/auth0/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
import { ConfigModule, MedusaContainer } from '@medusajs/medusa/dist/types/global';
import { AuthOptions, StrategyExport } from '../../types';
import { StrategyExport } from '../../types';
import { Router } from 'express';
import { getAuth0AdminAuthRouter, Auth0AdminStrategy } from './admin';
import { getAuth0StoreAuthRouter, Auth0StoreStrategy } from './store';
import { getAuth0AdminAuthRouter, getAuth0AdminStrategy } from './admin';
import { getAuth0StoreAuthRouter, getAuth0StoreStrategy } from './store';
import { Auth0Options } from './types';

export * from './admin';
export * from './store';
export * from './types';

export default {
load: (container: MedusaContainer, configModule: ConfigModule, options: AuthOptions): void => {
if (options.auth0?.admin) {
new Auth0AdminStrategy(container, configModule, options.auth0, options.strict);
load: (container, configModule, option): void => {
const id = option.identifier ?? option.type;
if (option.admin) {
const Clazz = getAuth0AdminStrategy(id);
new Clazz(container, configModule, option, option.strict);
}

if (options.auth0?.store) {
new Auth0StoreStrategy(container, configModule, options.auth0, options.strict);
if (option.store) {
const Clazz = getAuth0StoreStrategy(id);
new Clazz(container, configModule, option, option.strict);
}
},
getRouter: (configModule: ConfigModule, options: AuthOptions): Router[] => {
getRouter: (configModule, option): Router[] => {
const routers = [];
const id = option.identifier ?? option.type;

if (options.auth0?.admin) {
routers.push(getAuth0AdminAuthRouter(options.auth0, configModule));
if (option.admin) {
routers.push(getAuth0AdminAuthRouter(id, option, configModule));
}

if (options.auth0?.store) {
routers.push(getAuth0StoreAuthRouter(options.auth0, configModule));
if (option.store) {
routers.push(getAuth0StoreAuthRouter(id, option, configModule));
}

return routers;
},
} as StrategyExport;
} as StrategyExport<Auth0Options>;
Loading

1 comment on commit 65e7439

@vercel
Copy link

@vercel vercel bot commented on 65e7439 Dec 6, 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-adrien2p.vercel.app
medusa-plugins.vercel.app
medusa-plugins-git-main-adrien2p.vercel.app

Please sign in to comment.