@@ -11,11 +11,29 @@ const FormUrlEncoded = require('./FormUrlEncoded')
1111const IDToken = require ( './IDToken' )
1212const Session = require ( './Session' )
1313const onHttpError = require ( './onHttpError' )
14+ const HttpError = require ( 'standard-http-error' )
1415
1516/**
1617 * AuthenticationResponse
1718 */
1819class AuthenticationResponse {
20+ /**
21+ * @param rp {RelyingParty}
22+ * @param [redirect] {string} req.query
23+ * @param [body] {string} req.body.text
24+ * @param session {Session|Storage} req.session or localStorage or similar
25+ * @param params {object} hashmap
26+ * @param mode {string} 'query'/'fragment'/'form_post',
27+ * determined in `parseResponse()`
28+ */
29+ constructor ( { rp, redirect, body, session, mode, params = { } } ) {
30+ this . rp = rp
31+ this . redirect = redirect
32+ this . body = body
33+ this . session = session
34+ this . mode = mode
35+ this . params = params
36+ }
1937
2038 /**
2139 * validateResponse
@@ -24,14 +42,15 @@ class AuthenticationResponse {
2442 * Authentication response validation.
2543 *
2644 * @param {string|Object } response
27- * @returns {Promise }
45+ *
46+ * @returns {Promise<Session> }
2847 */
2948 static validateResponse ( response ) {
3049 return Promise . resolve ( response )
3150 . then ( this . parseResponse )
51+ . then ( this . errorResponse )
3252 . then ( this . matchRequest )
3353 . then ( this . validateStateParam )
34- . then ( this . errorResponse )
3554 . then ( this . validateResponseMode )
3655 . then ( this . validateResponseParams )
3756 . then ( this . exchangeAuthorizationCode )
@@ -42,15 +61,16 @@ class AuthenticationResponse {
4261 /**
4362 * parseResponse
4463 *
45- * @param {Object } response
46- * @returns {Promise }
64+ * @param {object } response
65+ *
66+ * @returns {object }
4767 */
4868 static parseResponse ( response ) {
4969 let { redirect, body} = response
5070
5171 // response must be either a redirect uri or request body, but not both
5272 if ( ( redirect && body ) || ( ! redirect && ! body ) ) {
53- throw new Error ( 'Invalid response mode' )
73+ throw new HttpError ( 400 , 'Invalid response mode' )
5474 }
5575
5676 // parse redirect uri
@@ -59,7 +79,7 @@ class AuthenticationResponse {
5979 let { search, hash} = url
6080
6181 if ( ( search && hash ) || ( ! search && ! hash ) ) {
62- throw new Error ( 'Invalid response mode' )
82+ throw new HttpError ( 400 , 'Invalid response mode' )
6383 }
6484
6585 if ( search ) {
@@ -82,6 +102,37 @@ class AuthenticationResponse {
82102 return response
83103 }
84104
105+ /**
106+ * errorResponse
107+ *
108+ * @param {AuthenticationResponse } response
109+ *
110+ * @throws {Error } If response params include the OAuth2 'error' param,
111+ * throws an error based on it.
112+ *
113+ * @returns {AuthenticationResponse } Chainable
114+ *
115+ * @todo Figure out HTTP status code (typically 400, 401 or 403)
116+ * based on the OAuth2/OIDC `error` code, probably using an external library
117+ */
118+ static errorResponse ( response ) {
119+ const errorCode = response . params . error
120+
121+ if ( errorCode ) {
122+ const errorParams = { }
123+ errorParams [ 'error' ] = errorCode
124+ errorParams [ 'error_description' ] = response . params [ 'error_description' ]
125+ errorParams [ 'error_uri' ] = response . params [ 'error_uri' ]
126+ errorParams [ 'state' ] = response . params [ 'state' ]
127+
128+ const error = new Error ( `AuthenticationResponse error: ${ errorCode } ` )
129+ error . info = errorParams
130+ throw error
131+ }
132+
133+ return response
134+ }
135+
85136 /**
86137 * matchRequest
87138 *
@@ -130,22 +181,6 @@ class AuthenticationResponse {
130181 } )
131182 }
132183
133- /**
134- * errorResponse
135- *
136- * @param {Object } response
137- * @returns {Promise }
138- */
139- static errorResponse ( response ) {
140- let error = response . params . error
141-
142- if ( error ) {
143- return Promise . reject ( error )
144- }
145-
146- return Promise . resolve ( response )
147- }
148-
149184 /**
150185 * validateResponseMode
151186 *
@@ -321,14 +356,25 @@ class AuthenticationResponse {
321356 /**
322357 * decodeIDToken
323358 *
324- * @param {Object } response
325- * @returns {Promise }
359+ * Note: If the `id_token` is not present in params, this method does not
360+ * get called (short-circuited in `validateIDToken()`).
361+ *
362+ * @param response {AuthenticationResponse}
363+ * @param response.params {object}
364+ * @param [response.params.id_token] {string} IDToken encoded as a JWT
365+ *
366+ * @returns {AuthenticationResponse } Chainable
326367 */
327368 static decodeIDToken ( response ) {
328369 let jwt = response . params . id_token
329370
330- if ( jwt ) {
371+ try {
331372 response . decoded = IDToken . decode ( jwt )
373+ } catch ( decodeError ) {
374+ const error = new HttpError ( 400 , 'Error decoding ID Token' )
375+ error . cause = decodeError
376+ error . info = { id_token : jwt }
377+ throw error
332378 }
333379
334380 return response
0 commit comments