Skip to content

Commit

Permalink
feat(oauth2): add pkce support
Browse files Browse the repository at this point in the history
fix #58
  • Loading branch information
nfroidure committed Oct 28, 2020
1 parent 4db35ea commit c755c90
Show file tree
Hide file tree
Showing 11 changed files with 13,132 additions and 4,326 deletions.
17,133 changes: 12,834 additions & 4,299 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion packages/whook-oauth2/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Object {
},
"a_grant_code",
"http://redirect.example.com/yolo",
undefined,
],
],
"oAuth2CodeCreateCalls": Array [],
Expand Down Expand Up @@ -173,7 +174,10 @@ Object {
"scope": "user",
},
"http://redirect.example.com/yolo?a_param=a_value",
Object {},
Object {
"codeChallenge": "",
"codeChallengeMethod": "plain",
},
],
],
"oAuth2PasswordCheckCalls": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ Object {
"redirectURI": "https://www.example.com",
"scope": "user",
},
Object {},
Object {
"codeChallenge": "",
"codeChallengeMethod": "plain",
},
],
],
"logCalls": Array [],
Expand Down
53 changes: 52 additions & 1 deletion packages/whook-oauth2/src/handlers/getOAuth2Authorize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
OAuth2GranterService,
} from '../services/oAuth2Granters';
import type { LogService } from 'common-services';
import { CODE_CHALLENGE_METHODS } from '../services/oAuth2CodeGranter';

/* Architecture Note #1: OAuth2 authorize
This endpoint simply redirect the user to the authentication
Expand Down Expand Up @@ -78,6 +79,29 @@ export const stateParameter: WhookAPIParameterDefinition = {
},
},
};
export const codeChallengeParameter: WhookAPIParameterDefinition = {
name: 'code_challenge',
parameter: {
in: 'query',
name: 'code_challenge',
required: false,
schema: {
type: 'string',
},
},
};
export const codeChallengeMethodParameter: WhookAPIParameterDefinition = {
name: 'code_challenge_method',
parameter: {
in: 'query',
name: 'code_challenge_method',
required: false,
schema: {
type: 'string',
enum: (CODE_CHALLENGE_METHODS as unknown) as string[],
},
},
};

export const definition: WhookAPIHandlerDefinition = {
method: 'get',
Expand All @@ -103,6 +127,12 @@ export const definition: WhookAPIHandlerDefinition = {
{
$ref: `#/components/parameters/${stateParameter.name}`,
},
{
$ref: `#/components/parameters/${codeChallengeParameter.name}`,
},
{
$ref: `#/components/parameters/${codeChallengeMethodParameter.name}`,
},
],
responses: {
'302': {
Expand Down Expand Up @@ -132,13 +162,17 @@ async function getOAuth2Authorize(
redirect_uri: demandedRedirectURI = '',
scope: demandedScope = '',
state,
code_challenge: codeChallenge = '',
code_challenge_method: codeChallengeMethod = 'plain',
...authorizeParameters
}: {
response_type: string;
client_id: string;
redirect_uri?: string;
scope?: string;
state: string;
code_challenge?: string;
code_challenge_method?: string;
authorizeParameters?: { [name: string]: unknown };
},
): Promise<WhookResponse> {
Expand All @@ -153,6 +187,15 @@ async function getOAuth2Authorize(
if (!granter) {
throw new YError('E_UNKNOWN_AUTHORIZER_TYPE', responseType);
}
if (responseType === 'code') {
if (!codeChallenge) {
if (OAUTH2.forcePKCE) {
throw new YError('E_PKCE_REQUIRED', responseType);
}
}
} else if (codeChallenge) {
throw new YError('E_PKCE_NOT_SUPPORTED', responseType);
}

const {
applicationId,
Expand All @@ -164,7 +207,15 @@ async function getOAuth2Authorize(
redirectURI: demandedRedirectURI,
scope: demandedScope,
},
camelCaseObjectProperties(authorizeParameters),
camelCaseObjectProperties({
...authorizeParameters,
...(responseType === 'code'
? {
codeChallenge,
codeChallengeMethod,
}
: {}),
}),
);

url.searchParams.set('type', responseType);
Expand Down
4 changes: 4 additions & 0 deletions packages/whook-oauth2/src/handlers/postOAuth2Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export const authorizationCodeTokenRequestBodySchema: WhookAPISchemaDefinition =
type: 'string',
format: 'uri',
},
code_verifier: {
type: 'string',
pattern: '^[\\d\\w\\-/\\._~]+$',
},
},
},
};
Expand Down
4 changes: 4 additions & 0 deletions packages/whook-oauth2/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
getOAuth2AuthorizeRedirectURIParameter,
getOAuth2AuthorizeScopeParameter,
getOAuth2AuthorizeStateParameter,
getOAuth2AuthorizeCodeChallengeParameter,
getOAuth2AuthorizeCodeChallengeMethodParameter,
initPostOAuth2Acknowledge,
postOAuth2AcknowledgeDefinition,
initPostOAuth2Token,
Expand Down Expand Up @@ -108,6 +110,8 @@ describe('OAuth2 server', () => {
getOAuth2AuthorizeRedirectURIParameter,
getOAuth2AuthorizeScopeParameter,
getOAuth2AuthorizeStateParameter,
getOAuth2AuthorizeCodeChallengeParameter,
getOAuth2AuthorizeCodeChallengeMethodParameter,
].reduce(
(parametersHash, { name, parameter }) => ({
...parametersHash,
Expand Down
13 changes: 12 additions & 1 deletion packages/whook-oauth2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import initGetOAuth2Authorize, {
redirectURIParameter as getOAuth2AuthorizeRedirectURIParameter,
scopeParameter as getOAuth2AuthorizeScopeParameter,
stateParameter as getOAuth2AuthorizeStateParameter,
codeChallengeParameter as getOAuth2AuthorizeCodeChallengeParameter,
codeChallengeMethodParameter as getOAuth2AuthorizeCodeChallengeMethodParameter,
} from './handlers/getOAuth2Authorize';
import initPostOAuth2Acknowledge, {
definition as postOAuth2AcknowledgeDefinition,
Expand All @@ -21,10 +23,14 @@ import initOAuth2Granters, {
OAUTH2_ERRORS_DESCRIPTORS,
} from './services/oAuth2Granters';
import initOAuth2ClientCredentialsGranter from './services/oAuth2ClientCredentialsGranter';
import initOAuth2CodeGranter from './services/oAuth2CodeGranter';
import initOAuth2CodeGranter, {
base64UrlEncode,
hashCodeVerifier,
} from './services/oAuth2CodeGranter';
import initOAuth2PasswordGranter from './services/oAuth2PasswordGranter';
import initOAuth2RefreshTokenGranter from './services/oAuth2RefreshTokenGranter';
import initOAuth2TokenGranter from './services/oAuth2TokenGranter';
import type { CodeChallengeMethod } from './services/oAuth2CodeGranter';
import type {
OAuth2CodeService,
OAuth2PasswordService,
Expand Down Expand Up @@ -56,6 +62,7 @@ import type {
} from './services/authCookies';

export type {
CodeChallengeMethod,
OAuth2CodeService,
OAuth2PasswordService,
OAuth2AccessTokenService,
Expand All @@ -77,6 +84,10 @@ export {
getOAuth2AuthorizeRedirectURIParameter,
getOAuth2AuthorizeScopeParameter,
getOAuth2AuthorizeStateParameter,
getOAuth2AuthorizeCodeChallengeParameter,
getOAuth2AuthorizeCodeChallengeMethodParameter,
base64UrlEncode,
hashCodeVerifier,
initPostOAuth2Acknowledge,
postOAuth2AcknowledgeDefinition,
initPostOAuth2Token,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Object {
},
"yolo",
"https://www.example.com/oauth2/code",
"",
],
],
"oAuth2CodeCreateCalls": Array [
Expand All @@ -42,7 +43,10 @@ Object {
"scope": "user",
},
"https://www.example.com/oauth2/code",
Object {},
Object {
"codeChallenge": "",
"codeChallengeMethod": "plain",
},
],
],
}
Expand Down
Loading

0 comments on commit c755c90

Please sign in to comment.