@opengovsg/mockpass 4.0.5 → 4.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -49,22 +49,40 @@ function config(app, { showLoginPage }) {
|
|
|
49
49
|
} = req.query
|
|
50
50
|
|
|
51
51
|
if (scope !== 'openid') {
|
|
52
|
-
return res.status(400).send(
|
|
52
|
+
return res.status(400).send({
|
|
53
|
+
error: 'invalid_scope',
|
|
54
|
+
error_description: `Unknown scope ${scope}`,
|
|
55
|
+
})
|
|
53
56
|
}
|
|
54
57
|
if (response_type !== 'code') {
|
|
55
|
-
return res.status(400).send(
|
|
58
|
+
return res.status(400).send({
|
|
59
|
+
error: 'unsupported_response_type',
|
|
60
|
+
error_description: `Unknown response_type ${response_type}`,
|
|
61
|
+
})
|
|
56
62
|
}
|
|
57
63
|
if (!client_id) {
|
|
58
|
-
return res.status(400).send(
|
|
64
|
+
return res.status(400).send({
|
|
65
|
+
error: 'invalid_request',
|
|
66
|
+
error_description: 'Missing client_id',
|
|
67
|
+
})
|
|
59
68
|
}
|
|
60
69
|
if (!redirectURI) {
|
|
61
|
-
return res.status(400).send(
|
|
70
|
+
return res.status(400).send({
|
|
71
|
+
error: 'invalid_request',
|
|
72
|
+
error_description: 'Missing redirect_uri',
|
|
73
|
+
})
|
|
62
74
|
}
|
|
63
75
|
if (!nonce) {
|
|
64
|
-
return res.status(400).send(
|
|
76
|
+
return res.status(400).send({
|
|
77
|
+
error: 'invalid_request',
|
|
78
|
+
error_description: 'Missing nonce',
|
|
79
|
+
})
|
|
65
80
|
}
|
|
66
81
|
if (!state) {
|
|
67
|
-
return res.status(400).send(
|
|
82
|
+
return res.status(400).send({
|
|
83
|
+
error: 'invalid_request',
|
|
84
|
+
error_description: 'Missing state',
|
|
85
|
+
})
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
// Identical to OIDC v1
|
|
@@ -128,27 +146,43 @@ function config(app, { showLoginPage }) {
|
|
|
128
146
|
|
|
129
147
|
// Only SP requires client_id
|
|
130
148
|
if (idp === 'singPass' && !client_id) {
|
|
131
|
-
return res.status(400).send(
|
|
149
|
+
return res.status(400).send({
|
|
150
|
+
error: 'invalid_request',
|
|
151
|
+
error_description: 'Missing client_id',
|
|
152
|
+
})
|
|
132
153
|
}
|
|
133
154
|
if (!redirectURI) {
|
|
134
|
-
return res.status(400).send(
|
|
155
|
+
return res.status(400).send({
|
|
156
|
+
error: 'invalid_request',
|
|
157
|
+
error_description: 'Missing redirect_uri',
|
|
158
|
+
})
|
|
135
159
|
}
|
|
136
160
|
if (grant_type !== 'authorization_code') {
|
|
137
|
-
return res.status(400).send(
|
|
161
|
+
return res.status(400).send({
|
|
162
|
+
error: 'unsupported_grant_type',
|
|
163
|
+
error_description: `Unknown grant_type ${grant_type}`,
|
|
164
|
+
})
|
|
138
165
|
}
|
|
139
166
|
if (!authCode) {
|
|
140
|
-
return res.status(400).send(
|
|
167
|
+
return res.status(400).send({
|
|
168
|
+
error: 'invalid_request',
|
|
169
|
+
error_description: 'Missing code',
|
|
170
|
+
})
|
|
141
171
|
}
|
|
142
172
|
if (
|
|
143
173
|
client_assertion_type !==
|
|
144
174
|
'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
145
175
|
) {
|
|
146
|
-
return res
|
|
147
|
-
|
|
148
|
-
|
|
176
|
+
return res.status(400).send({
|
|
177
|
+
error: 'invalid_request',
|
|
178
|
+
error_description: `Unknown client_assertion_type ${client_assertion_type}`,
|
|
179
|
+
})
|
|
149
180
|
}
|
|
150
181
|
if (!clientAssertion) {
|
|
151
|
-
return res.status(400).send(
|
|
182
|
+
return res.status(400).send({
|
|
183
|
+
error: 'invalid_request',
|
|
184
|
+
error_description: 'Missing client_assertion',
|
|
185
|
+
})
|
|
152
186
|
}
|
|
153
187
|
|
|
154
188
|
// Step 0: Get the RP keyset
|
|
@@ -165,11 +199,10 @@ function config(app, { showLoginPage }) {
|
|
|
165
199
|
method: 'GET',
|
|
166
200
|
}).then((response) => response.text())
|
|
167
201
|
} catch (e) {
|
|
168
|
-
return res
|
|
169
|
-
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
)
|
|
202
|
+
return res.status(400).send({
|
|
203
|
+
error: 'invalid_client',
|
|
204
|
+
error_description: `Failed to fetch RP JWKS from specified endpoint: ${e.message}`,
|
|
205
|
+
})
|
|
173
206
|
}
|
|
174
207
|
} else {
|
|
175
208
|
// If the endpoint is not defined, default to the sample keyset we provided.
|
|
@@ -180,7 +213,10 @@ function config(app, { showLoginPage }) {
|
|
|
180
213
|
try {
|
|
181
214
|
rpKeysetJson = JSON.parse(rpKeysetString)
|
|
182
215
|
} catch (e) {
|
|
183
|
-
return res.status(400).send(
|
|
216
|
+
return res.status(400).send({
|
|
217
|
+
error: 'invalid_client',
|
|
218
|
+
error_description: `Unable to parse RP keyset: ${e.message}`,
|
|
219
|
+
})
|
|
184
220
|
}
|
|
185
221
|
|
|
186
222
|
const rpKeyset = await jose.JWK.asKeyStore(rpKeysetJson)
|
|
@@ -192,33 +228,37 @@ function config(app, { showLoginPage }) {
|
|
|
192
228
|
rpKeyset,
|
|
193
229
|
).verify(clientAssertion)
|
|
194
230
|
} catch (e) {
|
|
195
|
-
return res
|
|
196
|
-
|
|
197
|
-
|
|
231
|
+
return res.status(400).send({
|
|
232
|
+
error: 'invalid_client',
|
|
233
|
+
error_description: `Unable to verify client_assertion: ${e.message}`,
|
|
234
|
+
})
|
|
198
235
|
}
|
|
199
236
|
|
|
200
237
|
let clientAssertionClaims
|
|
201
238
|
try {
|
|
202
239
|
clientAssertionClaims = JSON.parse(clientAssertionVerified.payload)
|
|
203
240
|
} catch (e) {
|
|
204
|
-
return res
|
|
205
|
-
|
|
206
|
-
|
|
241
|
+
return res.status(400).send({
|
|
242
|
+
error: 'invalid_client',
|
|
243
|
+
error_description: `Unable to parse client_assertion: ${e.message}`,
|
|
244
|
+
})
|
|
207
245
|
}
|
|
208
246
|
|
|
209
247
|
if (idp === 'singPass') {
|
|
210
248
|
if (clientAssertionClaims['sub'] !== client_id) {
|
|
211
|
-
return res
|
|
212
|
-
|
|
213
|
-
|
|
249
|
+
return res.status(400).send({
|
|
250
|
+
error: 'invalid_client',
|
|
251
|
+
error_description: 'Incorrect sub in client_assertion claims',
|
|
252
|
+
})
|
|
214
253
|
}
|
|
215
254
|
} else {
|
|
216
255
|
// Since client_id is not given for corpPass, sub claim is required in
|
|
217
256
|
// order to get aud for id_token.
|
|
218
257
|
if (!clientAssertionClaims['sub']) {
|
|
219
|
-
return res
|
|
220
|
-
|
|
221
|
-
|
|
258
|
+
return res.status(400).send({
|
|
259
|
+
error: 'invalid_client',
|
|
260
|
+
error_description: 'Missing sub in client_assertion claims',
|
|
261
|
+
})
|
|
222
262
|
}
|
|
223
263
|
}
|
|
224
264
|
|
|
@@ -228,9 +268,10 @@ function config(app, { showLoginPage }) {
|
|
|
228
268
|
)}/${idp.toLowerCase()}/v2`
|
|
229
269
|
|
|
230
270
|
if (clientAssertionClaims['aud'] !== iss) {
|
|
231
|
-
return res
|
|
232
|
-
|
|
233
|
-
|
|
271
|
+
return res.status(400).send({
|
|
272
|
+
error: 'invalid_client',
|
|
273
|
+
error_description: 'Incorrect aud in client_assertion claims',
|
|
274
|
+
})
|
|
234
275
|
}
|
|
235
276
|
|
|
236
277
|
// Step 1: Obtain profile for which the auth code requested data for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengovsg/mockpass",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.7",
|
|
4
4
|
"description": "A mock SingPass/CorpPass server for dev purposes",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"eslint-plugin-prettier": "^4.0.0",
|
|
61
61
|
"husky": "^8.0.1",
|
|
62
62
|
"lint-staged": "^13.0.3",
|
|
63
|
-
"nodemon": "^
|
|
63
|
+
"nodemon": "^3.0.1",
|
|
64
64
|
"pinst": "^3.0.0",
|
|
65
65
|
"prettier": "^2.0.5"
|
|
66
66
|
},
|
|
@@ -398,7 +398,7 @@ function redirectID() {
|
|
|
398
398
|
const optionsList = document.getElementById("id-datalist");
|
|
399
399
|
let optionsMap = new Map();
|
|
400
400
|
for (let i=0; i<optionsList.options.length; i++) {
|
|
401
|
-
optionsMap.set(optionsList.options[i].value, optionsList.options[i].dataset.
|
|
401
|
+
optionsMap.set(optionsList.options[i].value, optionsList.options[i].dataset.asserturl);
|
|
402
402
|
}
|
|
403
403
|
if (optionsMap.has(idInput.value)) {
|
|
404
404
|
const assertURL = optionsMap.get(idInput.value);
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
<input id="id-input" list="id-datalist" class="btn btn-secondary dropdown-toggle" oninput="redirectID()" placeholder= "Select Username" style="width: 100%; border: 2px solid #ccc; border-radius: 5px; background: white; color: rgb(42, 45, 51); text-align: left;">
|
|
170
170
|
<datalist class="dropdown-menu" id="id-datalist" aria-labelledby="id-input" >
|
|
171
171
|
{{#values}}
|
|
172
|
-
<option value="{{ id }}" data-
|
|
172
|
+
<option value="{{ id }}" data-asserturl="{{ assertURL }}">{{ id }}</option>
|
|
173
173
|
{{/values}}
|
|
174
174
|
</datalist>
|
|
175
175
|
</div>
|