Skip to content

Commit 3f767d6

Browse files
authored
Merge pull request #99 from GovTechSG/update-tests-pkce-login
[MISC][RG] update convenience test suites with PKCE login flow
2 parents 37c5f08 + e2f311a commit 3f767d6

File tree

5 files changed

+71
-19
lines changed

5 files changed

+71
-19
lines changed

.env.sample

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# ==============================================================================
2+
# MyInfo
3+
# ==============================================================================
14
SINGPASS_ESERVICE_ID=
25
MY_INFO_ENVIRONMENT=
36
MY_INFO_PUBLIC_CERT=

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelogs
22

3+
## x.x.x
4+
5+
- Update convenience test suite to include SingPass login with PKCE
6+
37
## 8.4.6
48

59
- Add `codeVerifier` for new singpass integration

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ Use this module to build client applications that can:
1313

1414
---
1515

16+
## Testing your SingPass / MyInfo client setup and secrets
17+
18+
- These tests will allow you to check if your IDs / keys / certs / JWK endpoints/objects
19+
are correctly setup
20+
- Use `singpass-helper-ndi-ext.spec.ts` to test login and retrieval of tokens
21+
- After logging in use `myinfo.ext.spec.ts` to test retrieval of myinfo data
22+
1623
## MyInfo
1724

1825
---

src/myinfo/helper/__tests__/myinfo.ext.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { configs } from "./test.configs";
33
import { set } from "lodash";
44
import { aliasName } from "../../fake/profiles/common";
55

6+
/**
7+
* Convenience tests for MyInfo config / integration
8+
* 1. fill in the `testNric`
9+
* 2. .env variables must be set, make a copy of `.env.sample`
10+
* 3. `singpassEserviceID` is not the SingPass AppId. It looks like STG-T12345678-LOGIN-xxxxxxxx
11+
*/
612
describe("MyInfoClient", () => {
713
describe("STAGING Person basic API V3", () => {
814
it("should use the available env variables, call myinfo, and get back person", async () => {

src/singpass/__tests__/singpass-helper-ndi.ext.spec.ts

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import { KeyFormat } from "../../util/KeyUtil";
33
import { logger } from "../../util/Logger";
44
import { NdiOidcHelper, NdiOidcHelperConstructor } from "../singpass-helper-ndi";
55
import { configs } from "./test.configs";
6+
import { generators } from "openid-client";
7+
const expectedUinfin = ""; // FIXME: fill in with expected uinfin
68

79
/**
810
* Convenience tests for NDI OIDC config / integration
9-
* - Use the first test to generate auth url and login with your account
10-
* - then fill up info on second test to verify your setup/keys work
11-
* - if public keys are not yet deployed to well known endpoint one will need to set the app on SP portal to use JWKS object
12-
* .env variables must be set
11+
* 1. Fill up the FIXME: for expectedUinfin
12+
* 2. Use the first test to generate auth url and login with your account, then fill up FIXME:
13+
* on second test to verify your setup/keys work (authcode comes from the `code` param in the redirect after login)
14+
* 3. If public keys are not yet deployed to well known endpoint (or it's not possible to expose such endpoints),
15+
* one will need to set the client app on SP dev portal to use JWKS object
16+
* 4. .env variables must be set, make a copy of .env.sample
1317
*/
1418
describe.skip("Singpass NDI OIDC integration", () => {
1519
const props: NdiOidcHelperConstructor = {
@@ -26,33 +30,61 @@ describe.skip("Singpass NDI OIDC integration", () => {
2630
},
2731
};
2832

29-
fdescribe("construct authorization url", () => {
30-
// extract the code after successful login and browser redirect from the callback
31-
// {callbackEndpoint}?code={CODE-HERE}&state=xxx
33+
describe("integration tester for Ndi OIDC", () => {
34+
describe("construct authorization url", () => {
3235
it("should successfully construct authorization url", async () => {
33-
const spNdiHelper = new NdiOidcHelper(props);
34-
const nonce = Date.now() + "";
35-
const state = nonce + "-state";
36-
const authorizationUrl = await spNdiHelper.constructAuthorizationUrl(state, nonce);
37-
logger.debug("authorizationUrl: ", authorizationUrl);
36+
await constructAuthorizationUrlHelper();
37+
});
38+
});
39+
40+
describe("get user identity after successful redirect with auth code", () => {
41+
it("should successfully extract nric and uuid", async () => {
42+
const authCode = ""; // FIXME: fill in with authCode
43+
const nric = await extractNricHelper(authCode);
44+
expect(nric).toMatch(expectedUinfin);
45+
});
3846
});
3947
});
4048

41-
xdescribe("get user identity after successful redirect with auth code", () => {
49+
describe("integration tester for Ndi OIDC with PKCE", () => {
50+
describe("construct authorization url", () => {
51+
it("should successfully construct authorization url", async () => {
52+
await constructAuthorizationUrlHelper(true);
53+
});
54+
});
55+
56+
describe("get user identity after successful redirect with auth code", () => {
4257
it("should successfully extract nric and uuid", async () => {
43-
const authCode = ""; // FIXME: fill in with authcode
44-
const expectedUinfin = ""; // FIXME: fill in with expected uinfin
58+
const authCode = ""; // FIXME: fill in with authCode
59+
const codeVerifier = ""; // FIXME: fill in with codeVerifier
60+
const nric = await extractNricHelper(authCode, codeVerifier);
61+
expect(nric).toMatch(expectedUinfin);
62+
});
63+
});
64+
});
65+
66+
const constructAuthorizationUrlHelper = async (pkceFlow?: boolean) => {
67+
const spNdiHelper = new NdiOidcHelper(props);
68+
const nonce = Date.now() + "";
69+
const state = nonce + "-state";
70+
const codeVerifier = pkceFlow ? generators.codeVerifier() : undefined;
71+
const authorizationUrl = await spNdiHelper.constructAuthorizationUrl(state, nonce, codeVerifier);
72+
logger.debug("authorizationUrl: ", authorizationUrl);
73+
codeVerifier && logger.debug("codeVerifier: ", codeVerifier);
74+
};
75+
76+
const extractNricHelper = async (authCode: string, codeVerifier?: string) => {
4577
const spNdiHelper = new NdiOidcHelper(props);
4678

47-
const tokens = await spNdiHelper.getTokens(authCode);
79+
const tokens = await spNdiHelper.getTokens(authCode, codeVerifier);
4880
logger.debug("tokens", tokens);
4981

5082
const tokenPayload = await spNdiHelper.getIdTokenPayload(tokens);
5183
logger.debug("tokenPayload", tokenPayload);
5284

5385
const { nric } = await spNdiHelper.extractNricAndUuidFromPayload(tokenPayload);
5486
logger.debug("nric", nric);
55-
expect(nric).toMatch(expectedUinfin);
56-
});
57-
});
87+
88+
return nric;
89+
};
5890
});

0 commit comments

Comments
 (0)