Skip to content

Commit c3eceba

Browse files
committed
PKCE refactor, add PKCEEnabled option, require client.isPublic property with PKCE
1 parent d40173d commit c3eceba

File tree

4 files changed

+20
-7
lines changed

4 files changed

+20
-7
lines changed

lib/grant-types/authorization-code-grant-type.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,27 @@ AuthorizationCodeGrantType.prototype.getAuthorizationCode = function(request, cl
124124

125125
if (this.PKCEEnabled && client.isPublic) {
126126
if (!code.codeChallenge) {
127-
throw new ServerError('Server error: `getAuthorizationCode()` did not return a `codeChallenge` property');
127+
throw new InvalidGrantError('Invalid grant: code challenge is invalid');
128128
}
129129

130+
/**
131+
* The "code_challenge_method" is bound to the Authorization Code when
132+
* the Authorization Code is issued. That is the method that the token
133+
* endpoint MUST use to verify the "code_verifier".
134+
*/
130135
if (!code.codeChallengeMethod) {
131136
throw new ServerError('Server error: `getAuthorizationCode()` did not return a `codeChallengeMethod` property');
132137
}
133138

134139
if (code.codeChallengeMethod === 'plain' && code.codeChallenge !== request.body.code_verifier) {
135-
throw new InvalidGrantError('Invalid grant: `code_verifier` is invalid');
140+
throw new InvalidGrantError('Invalid grant: code verifier is invalid');
136141
}
137142

138143
if (code.codeChallengeMethod === 'S256') {
139144
var hash = stringUtil.base64URLEncode(crypto.createHash('sha256').update(request.body.code_verifier).digest());
140145

141146
if (code.codeChallenge !== hash) {
142-
throw new InvalidGrantError('Invalid grant: `code_verifier` is invalid');
147+
throw new InvalidGrantError('Invalid grant: code verifier is invalid');
143148
}
144149
}
145150
}

lib/handlers/authorize-handler.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ AuthorizeHandler.prototype.generateAuthorizationCode = function(client, user, sc
151151
AuthorizeHandler.prototype.getCodeChallenge = function(request, client) {
152152
var codeChallenge = request.body.code_challenge || request.query.code_challenge;
153153

154+
/**
155+
* If the server requires Proof Key for Code Exchange (PKCE) by OAuth
156+
* public clients and the client does not send the "code_challenge" in
157+
* the request, the authorization endpoint MUST return the authorization
158+
* error response with the "error" value set to "invalid_request". The
159+
* "error_description" or the response of "error_uri" SHOULD explain the
160+
* nature of error, e.g., code challenge required.
161+
*/
154162
if (this.PKCEEnabled && client.isPublic && _.isEmpty(codeChallenge)) {
155163
throw new InvalidRequestError('Missing parameter: `code_challenge`. Public clients must include a code_challenge');
156164
}
@@ -162,7 +170,7 @@ AuthorizeHandler.prototype.getCodeChallengeMethod = function(request) {
162170
var codeChallengeMethod = request.body.code_challenge_method || request.query.code_challenge_method || 'plain';
163171

164172
if (!_.includes(['S256', 'plain'], codeChallengeMethod)) {
165-
throw new InvalidRequestError('Invalid parameter: `code_challenge_method`');
173+
throw new InvalidRequestError('Invalid parameter: `code_challenge_method`, use S256 instead');
166174
}
167175

168176
return codeChallengeMethod;

test/integration/grant-types/authorization-code-grant-type_test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ describe('AuthorizationCodeGrantType integration', function() {
386386
.then(should.fail)
387387
.catch(function(e) {
388388
e.should.be.an.instanceOf(InvalidGrantError);
389-
e.message.should.equal('Invalid grant: `code_verifier` is invalid');
389+
e.message.should.equal('Invalid grant: code verifier is invalid');
390390
});
391391
});
392392

@@ -412,7 +412,7 @@ describe('AuthorizationCodeGrantType integration', function() {
412412
.then(should.fail)
413413
.catch(function(e) {
414414
e.should.be.an.instanceOf(InvalidGrantError);
415-
e.message.should.equal('Invalid grant: `code_verifier` is invalid');
415+
e.message.should.equal('Invalid grant: code verifier is invalid');
416416
});
417417
});
418418

test/integration/handlers/authorize-handler_test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ describe('AuthorizeHandler integration', function() {
908908
should.fail();
909909
} catch (e) {
910910
e.should.be.an.instanceOf(InvalidRequestError);
911-
e.message.should.equal('Invalid parameter: `code_challenge_method`');
911+
e.message.should.equal('Invalid parameter: `code_challenge_method`, use S256 instead');
912912
}
913913
});
914914
describe('with `code_challenge_method` in the request body', function() {

0 commit comments

Comments
 (0)