Skip to content

Commit

Permalink
Merge pull request #849 from cam-inc/feat/auth-oidc
Browse files Browse the repository at this point in the history
feat(nodejs): auth oidc
  • Loading branch information
takoring authored Jan 30, 2025
2 parents 002919c + 310c594 commit 46a2962
Show file tree
Hide file tree
Showing 43 changed files with 2,908 additions and 376 deletions.
7 changes: 7 additions & 0 deletions example/nodejs/.env.template
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
GOOGLE_OAUTH2_CLIENT_ID=
GOOGLE_OAUTH2_CLIENT_SECRET=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_CLIENT_CONFIGURATION_URL=
AWS_S3_ACCESS_KEY_ID=
AWS_S3_SECRET_KEY=
SSL_CERTIFICATE=
SSL_PRIVATE_KEY=
SERVICE_ENV=
MODE=
3 changes: 2 additions & 1 deletion example/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "dist/server.js",
"dependencies": {
"@aws-sdk/client-s3": "^3.451.0",
"@viron/lib": "^2.0.2",
"@viron/lib": "2.4.0-alpha.0",
"accepts": "^1.3.7",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
Expand All @@ -20,6 +20,7 @@
"multer": "^1.4.3",
"multer-s3": "^3.0.1",
"mysql2": "^2.2.5",
"openid-client": "^4.7.4",
"pino": "^7.6.4",
"pino-http": "^6.6.0",
"sequelize": "^6.5.0",
Expand Down
7 changes: 7 additions & 0 deletions example/nodejs/src/config/development.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export const get = (): Config => {
additionalScopes: [],
userHostedDomains: ['cam-inc.co.jp', 'cyberagent.co.jp'],
},
oidc: {
clientId: process.env.OIDC_CLIENT_ID ?? '',
clientSecret: process.env.OIDC_CLIENT_SECRET ?? '',
configurationUrl: process.env.OIDC_CLIENT_CONFIGURATION_URL ?? '',
additionalScopes: [],
userHostedDomains: ['cam-inc.co.jp', 'cyberagent.co.jp'],
},
},
aws: {
s3: {
Expand Down
1 change: 1 addition & 0 deletions example/nodejs/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface Config {
auth: {
jwt: domainsAuth.JwtConfig;
googleOAuth2: domainsAuth.GoogleOAuthConfig;
oidc: domainsAuth.OidcConfig;
};
aws: AWSConfig;
oas: OasConfig;
Expand Down
7 changes: 7 additions & 0 deletions example/nodejs/src/config/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export const get = (mode: Mode): Config => {
additionalScopes: [],
userHostedDomains: ['cam-inc.co.jp', 'cyberagent.co.jp'],
},
oidc: {
clientId: process.env.OIDC_CLIENT_ID ?? '',
clientSecret: process.env.OIDC_CLIENT_SECRET ?? '',
configurationUrl: process.env.OIDC_CLIENT_CONFIGURATION_URL ?? '',
additionalScopes: [],
userHostedDomains: ['cam-inc.co.jp', 'cyberagent.co.jp'],
},
},
aws: {
s3: {
Expand Down
7 changes: 7 additions & 0 deletions example/nodejs/src/config/production.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ export const get = (): Config => {
additionalScopes: [],
userHostedDomains: ['gmail.com', 'cam-inc.co.jp', 'cyberagent.co.jp'],
},
oidc: {
clientId: process.env.OIDC_CLIENT_ID ?? '',
clientSecret: process.env.OIDC_CLIENT_SECRET ?? '',
configurationUrl: process.env.OIDC_CLIENT_CONFIGURATION_URL ?? '',
additionalScopes: [],
userHostedDomains: ['cam-inc.co.jp', 'cyberagent.co.jp'],
},
},
aws: {
s3: {
Expand Down
4 changes: 2 additions & 2 deletions example/nodejs/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ export const MODE = {
MYSQL: 'mysql',
MONGO: 'mongo',
} as const;
export type Mode = typeof MODE[keyof typeof MODE];
export type Mode = (typeof MODE)[keyof typeof MODE];
export type StoreType = Mode;

export const SERVICE_ENV = {
LOCAL: 'local',
DEVELOPMENT: 'development',
PRODUCTION: 'production',
};
export type ServiceEnv = typeof SERVICE_ENV[keyof typeof SERVICE_ENV];
export type ServiceEnv = (typeof SERVICE_ENV)[keyof typeof SERVICE_ENV];

export const AUTHENTICATION_RESULT_TYPE = {
SUCCESS: 'success',
Expand Down
69 changes: 69 additions & 0 deletions example/nodejs/src/controllers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
domainsAuth,
genAuthorizationCookie,
genOAuthStateCookie,
genOidcStateCookie,
genOidcCodeVerifierCookie,
mismatchState,
COOKIE_KEY,
HTTP_HEADER,
Expand All @@ -28,6 +30,73 @@ export const signinEmail = async (context: RouteContext): Promise<void> => {
context.res.status(204).end();
};

// OIDCの認証画面へリダイレクト
export const oidcAuthorization = async (
context: RouteContext
): Promise<void> => {
const { redirectUri } = context.params.query;
const state = domainsAuth.genState();
const client = await domainsAuth.genOidcClient(
ctx.config.auth.oidc,
redirectUri
);

// PKCE用のCodeVerifierを生成
const codeVerifier = await domainsAuth.genOidcCodeVerifier();

// OIDC認証画面URLを取得
const authorizationUrl = await domainsAuth.getOidcAuthorizationUrl(
ctx.config.auth.oidc,
client,
codeVerifier,
state
);

// CookieにOIDCのStateとPKCE用のCodeVerifierをセット
const cookies = [
genOidcStateCookie(state),
genOidcCodeVerifierCookie(codeVerifier),
];
context.res.setHeader(HTTP_HEADER.SET_COOKIE, cookies);
context.res.setHeader(HTTP_HEADER.LOCATION, authorizationUrl);
context.res.status(301).end();
};

// OIDCのコールバック
export const oidcCallback = async (context: RouteContext): Promise<void> => {
const codeVerifier = context.req.cookies[COOKIE_KEY.OIDC_CODE_VERIFIER];
const cookieState = context.req.cookies[COOKIE_KEY.OIDC_STATE];
const { state, redirectUri } = context.requestBody;

if (!codeVerifier || !cookieState || !state || cookieState !== state) {
throw mismatchState();
}

// OIDC Clientを取得
const client = await domainsAuth.genOidcClient(
ctx.config.auth.oidc,
redirectUri
);
const params = client.callbackParams(context.req);
const token = await domainsAuth.signinOidc(
client,
codeVerifier as string,
redirectUri,
params,
ctx.config.auth.oidc
);
context.res.setHeader(
HTTP_HEADER.SET_COOKIE,
genAuthorizationCookie(token, {
maxAge: ctx.config.auth.jwt.expirationSec,
})
);
context.origRes.clearCookie(COOKIE_KEY.OIDC_STATE);
context.origRes.clearCookie(COOKIE_KEY.OIDC_CODE_VERIFIER);

context.res.status(204).end();
};

// GoogleOAuth2の認可画面へリダイレクト
export const oauth2GoogleAuthorization = async (
context: RouteContext
Expand Down
14 changes: 14 additions & 0 deletions example/nodejs/src/controllers/authconfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
SIGNOUT_PATH,
OAUTH2_GOOGLE_AUTHORIZATION_PATH,
OAUTH2_GOOGLE_CALLBACK_PATH,
OIDC_AUTHORIZATION_PATH,
OIDC_CALLBACK_PATH,
} from '@viron/lib';
import { RouteContext } from '../application';

Expand Down Expand Up @@ -35,6 +37,18 @@ export const listVironAuthconfigs = async (
method: API_METHOD.POST,
path: OAUTH2_GOOGLE_CALLBACK_PATH,
},
{
provider: AUTH_CONFIG_PROVIDER.OIDC,
type: AUTH_CONFIG_TYPE.OIDC,
method: API_METHOD.GET,
path: OIDC_AUTHORIZATION_PATH,
},
{
provider: AUTH_CONFIG_PROVIDER.OIDC,
type: AUTH_CONFIG_TYPE.OIDC_CALLBACK,
method: API_METHOD.POST,
path: OIDC_CALLBACK_PATH,
},
{
provider: AUTH_CONFIG_PROVIDER.SIGNOUT,
type: AUTH_CONFIG_TYPE.SIGNOUT,
Expand Down
9 changes: 9 additions & 0 deletions example/nodejs/src/infrastructures/mysql/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import * as articles from './articles';
import * as items from './items';
import * as itemDetails from './item_details';
import * as medias from './medias';
import * as vironLibAminUsers from '@viron/lib/src/infrastructures/mysql/models/adminusers';
import * as vironLibAuitLogs from '@viron/lib/src/infrastructures/mysql/models/auditlogs';
import * as vironLibRevokedTokens from '@viron/lib/src/infrastructures/mysql/models/revokedtokens';

export interface MysqlModels {
[users.name]: users.UserModelStatic;
Expand All @@ -13,6 +16,9 @@ export interface MysqlModels {
[items.name]: items.ItemModelStatic;
[itemDetails.name]: itemDetails.ItemDetailModelStatic;
[medias.name]: medias.MediaModelStatic;
[vironLibAminUsers.name]: vironLibAminUsers.AdminUserModelStatic;
[vironLibAuitLogs.name]: vironLibAuitLogs.AuditLogModelStatic;
[vironLibRevokedTokens.name]: vironLibRevokedTokens.RevokedTokenModelStatic;
}

// Get models
Expand All @@ -24,6 +30,9 @@ export const models = (s: Sequelize): MysqlModels => {
[items.name]: items.createModel(s),
[itemDetails.name]: itemDetails.createModel(s),
[medias.name]: medias.createModel(s),
[vironLibAminUsers.name]: vironLibAminUsers.createModel(s),
[vironLibAuitLogs.name]: vironLibAuitLogs.createModel(s),
[vironLibRevokedTokens.name]: vironLibRevokedTokens.createModel(s),
};

mysqlModels[items.name].belongsTo(mysqlModels[itemDetails.name], {
Expand Down
15 changes: 15 additions & 0 deletions example/nodejs/src/security_handlers/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ export const jwt = async (
}
break;
}
case AUTH_TYPE.OIDC: {
// OIDC認証の場合はアクセストークンの検証
const client = await domainsAuth.genOidcClient(ctx.config.auth.oidc);
if (
await domainsAuth.verifyOidcAccessToken(
client,
ctx.config.auth.oidc,
userId,
user
)
) {
return authSuccess(user);
}
break;
}
default:
return authSuccess(user);
}
Expand Down
Loading

0 comments on commit 46a2962

Please sign in to comment.