2
2
* nginScript functions for providing OpenID Connect authorization
3
3
* code flow with NGINX Plus.
4
4
*
5
- * Copyright (C) 2018 Nginx, Inc.
5
+ * Copyright (C) 2019 Nginx, Inc.
6
6
*/
7
7
8
8
var auth_token = "" ;
9
9
10
- function oidcCodeExchange ( req , res ) {
10
+ function oidcCodeExchange ( r ) {
11
11
// First check that we received an authorization code from the IdP
12
- if ( req . variables . arg_code . length == 0 ) {
13
- if ( req . variables . arg_error ) {
14
- req . error ( "OIDC error receiving authorization code from IdP: " + req . variables . arg_error_description ) ;
12
+ if ( r . variables . arg_code . length == 0 ) {
13
+ if ( r . variables . arg_error ) {
14
+ r . error ( "OIDC error receiving authorization code from IdP: " + r . variables . arg_error_description ) ;
15
15
} else {
16
- req . error ( "OIDC expected authorization code from IdP but received: " + req . variables . uri ) ;
16
+ r . error ( "OIDC expected authorization code from IdP but received: " + r . variables . uri ) ;
17
17
}
18
- res . return ( 502 ) ;
18
+ r . return ( 502 ) ;
19
19
return ;
20
20
}
21
21
22
22
// Pass the authorization code to the /_token location so that it can be
23
23
// proxied to the IdP in exchange for a JWT
24
- req . subrequest ( "/_token" , "code=" + req . variables . arg_code ,
24
+ r . subrequest ( "/_token" , "code=" + r . variables . arg_code ,
25
25
function ( reply ) {
26
26
if ( reply . status == 504 ) {
27
- req . error ( "OIDC timeout connecting to IdP when sending authorization code" ) ;
28
- res . return ( 504 ) ;
27
+ r . error ( "OIDC timeout connecting to IdP when sending authorization code" ) ;
28
+ r . return ( 504 ) ;
29
29
return ;
30
30
}
31
31
32
32
if ( reply . status != 200 ) {
33
33
try {
34
- var errorset = JSON . parse ( reply . body ) ;
34
+ var errorset = JSON . parse ( reply . responseBody ) ;
35
35
if ( errorset . error ) {
36
- req . error ( "OIDC error from IdP when sending authorization code: " + errorset . error + ", " + errorset . error_description ) ;
36
+ r . error ( "OIDC error from IdP when sending authorization code: " + errorset . error + ", " + errorset . error_description ) ;
37
37
} else {
38
- req . error ( "OIDC unexpected response from IdP when sending authorization code (HTTP " + reply . status + "). " + reply . body ) ;
38
+ r . error ( "OIDC unexpected response from IdP when sending authorization code (HTTP " + reply . status + "). " + reply . responseBody ) ;
39
39
}
40
40
} catch ( e ) {
41
- req . error ( "OIDC unexpected response from IdP when sending authorization code (HTTP " + reply . status + "). " + reply . body ) ;
41
+ r . error ( "OIDC unexpected response from IdP when sending authorization code (HTTP " + reply . status + "). " + reply . responseBody ) ;
42
42
}
43
- res . return ( 502 ) ;
43
+ r . return ( 502 ) ;
44
44
return ;
45
45
}
46
46
47
47
// Code exchange returned 200, check response
48
48
try {
49
49
// Send the ID Token to auth_jwt location for validation
50
- var tokenset = JSON . parse ( reply . body ) ;
51
- req . subrequest ( "/_id_token_validation" , "token=" + tokenset . id_token ,
50
+ var tokenset = JSON . parse ( reply . responseBody ) ;
51
+ r . subrequest ( "/_id_token_validation" , "token=" + tokenset . id_token ,
52
52
function ( reply ) {
53
53
if ( reply . status != 204 ) {
54
- res . return ( 500 ) ; // validateIdToken() will log errors
54
+ r . return ( 500 ) ; // validateIdToken() will log errors
55
55
return ;
56
56
}
57
57
58
58
// ID Token is valid
59
- req . subrequest ( "/_create_session" , "key=" + req . variables . request_id + "&val=" + tokenset . id_token ,
59
+ r . subrequest ( "/_create_session" , "key=" + r . variables . request_id + "&val=" + tokenset . id_token ,
60
60
function ( reply ) {
61
61
if ( reply . status != 201 ) {
62
- req . error ( "OIDC error creating session in keyval (" + reply . status + ") " + reply . body ) ;
63
- res . return ( 500 ) ;
62
+ r . error ( "OIDC error creating session in keyval (" + reply . status + ") " + reply . responseBody ) ;
63
+ r . return ( 500 ) ;
64
64
return ;
65
65
}
66
66
67
67
// Session created
68
- req . log ( "OIDC success, creating session " + req . variables . request_id ) ;
69
- auth_token = req . variables . request_id ; // Export as NGINX variable
70
- res . return ( 302 , req . variables . cookie_auth_redir ) ;
68
+ r . log ( "OIDC success, creating session " + r . variables . request_id ) ;
69
+ auth_token = r . variables . request_id ; // Export as NGINX variable
70
+ r . return ( 302 , r . variables . cookie_auth_redir ) ;
71
71
}
72
72
) ;
73
73
}
74
74
) ;
75
75
} catch ( e ) {
76
- req . error ( "OIDC authorization code sent but token response is not JSON. " + reply . body ) ;
77
- res . return ( 502 ) ;
76
+ r . error ( "OIDC authorization code sent but token response is not JSON. " + reply . responseBody ) ;
77
+ r . return ( 502 ) ;
78
78
}
79
79
}
80
80
) ;
81
81
}
82
82
83
- function getAuthToken ( req , res ) {
83
+ function getAuthToken ( r ) {
84
84
return auth_token ;
85
85
}
86
86
87
- function hashRequestId ( req ) {
87
+ function hashRequestId ( r ) {
88
88
var c = require ( 'crypto' ) ;
89
- var h = c . createHmac ( 'sha256' , req . variables . oidc_hmac_key ) . update ( req . variables . request_id ) ;
89
+ var h = c . createHmac ( 'sha256' , r . variables . oidc_hmac_key ) . update ( r . variables . request_id ) ;
90
90
return h . digest ( 'base64url' ) ;
91
91
}
92
92
93
- function validateIdToken ( req , res ) {
93
+ function validateIdToken ( r ) {
94
94
// Check mandatory claims
95
95
var required_claims = [ "aud" , "iat" , "iss" , "sub" ] ;
96
96
var missing_claims = [ ] ;
97
97
for ( var i in required_claims ) {
98
- if ( req . variables [ "jwt_claim_" + required_claims [ i ] ] . length == 0 ) {
98
+ if ( r . variables [ "jwt_claim_" + required_claims [ i ] ] . length == 0 ) {
99
99
missing_claims . push ( required_claims [ i ] ) ;
100
100
}
101
101
}
102
102
if ( missing_claims . length ) {
103
- req . error ( "OIDC ID Token validation error: missing claim(s) " + missing_claims . join ( " " ) ) ;
104
- res . return ( 403 ) ;
103
+ r . error ( "OIDC ID Token validation error: missing claim(s) " + missing_claims . join ( " " ) ) ;
104
+ r . return ( 403 ) ;
105
105
return ;
106
106
}
107
107
var valid_token = true ;
108
108
109
109
// Check iat is a positive integer
110
- var iat = Math . floor ( Number ( req . variables . jwt_claim_iat ) ) ;
111
- if ( String ( iat ) != req . variables . jwt_claim_iat || iat < 1 ) {
112
- req . error ( "OIDC ID Token validation error: iat claim is not a valid number" ) ;
110
+ var iat = Math . floor ( Number ( r . variables . jwt_claim_iat ) ) ;
111
+ if ( String ( iat ) != r . variables . jwt_claim_iat || iat < 1 ) {
112
+ r . error ( "OIDC ID Token validation error: iat claim is not a valid number" ) ;
113
113
valid_token = false ;
114
114
}
115
115
116
116
// Check iss relates to $oidc_authz_endpoint
117
- if ( ! req . variables . oidc_authz_endpoint . startsWith ( req . variables . jwt_claim_iss ) ) {
118
- req . error ( "OIDC ID Token validation error: iss claim (" + req . variables . jwt_claim_iss + ") is not found in $oidc_authz_endpoint" ) ;
117
+ if ( ! r . variables . oidc_authz_endpoint . startsWith ( r . variables . jwt_claim_iss ) ) {
118
+ r . error ( "OIDC ID Token validation error: iss claim (" + r . variables . jwt_claim_iss + ") is not found in $oidc_authz_endpoint" ) ;
119
119
valid_token = false ;
120
120
}
121
121
122
122
// Audience matching
123
- if ( req . variables . jwt_claim_aud != req . variables . oidc_client ) {
124
- req . error ( "OIDC ID Token validation error: aud claim (" + req . variables . jwt_claim_aud + ") does not match $oidc_client" ) ;
123
+ if ( r . variables . jwt_claim_aud != r . variables . oidc_client ) {
124
+ r . error ( "OIDC ID Token validation error: aud claim (" + r . variables . jwt_claim_aud + ") does not match $oidc_client" ) ;
125
125
valid_token = false ;
126
126
}
127
127
128
128
// If we receive a nonce in the ID Token then we will use the auth_nonce cookie
129
129
// to check that the JWT can be validated as being directly related to the
130
130
// original request by this client. This mitigates against token replay attacks.
131
131
var client_nonce_hash = "" ;
132
- if ( req . variables . cookie_auth_nonce ) {
132
+ if ( r . variables . cookie_auth_nonce ) {
133
133
var c = require ( 'crypto' ) ;
134
- var h = c . createHmac ( 'sha256' , req . variables . oidc_hmac_key ) . update ( req . variables . cookie_auth_nonce ) ;
134
+ var h = c . createHmac ( 'sha256' , r . variables . oidc_hmac_key ) . update ( r . variables . cookie_auth_nonce ) ;
135
135
client_nonce_hash = h . digest ( 'base64url' ) ;
136
136
}
137
- if ( req . variables . jwt_claim_nonce != client_nonce_hash ) {
138
- req . error ( "OIDC ID Token validation error: nonce mismatch" ) ;
137
+ if ( r . variables . jwt_claim_nonce != client_nonce_hash ) {
138
+ r . error ( "OIDC ID Token validation error: nonce mismatch" ) ;
139
139
valid_token = false ;
140
140
}
141
141
142
142
if ( valid_token ) {
143
- res . return ( 204 ) ;
143
+ r . return ( 204 ) ;
144
144
} else {
145
- res . return ( 403 ) ;
145
+ r . return ( 403 ) ;
146
146
}
147
- }
147
+ }
0 commit comments