11import { AxiosInstance , AxiosProxyConfig } from "axios" ;
2+ import { generators } from "openid-client" ;
23import * as querystringUtil from "querystring" ;
34import { createClient } from "../client/axios-client" ;
45import { JweUtil } from "../util" ;
56import { SingpassMyInfoError } from "../util/error/SingpassMyinfoError" ;
6- import { logger } from "../util/Logger" ;
7- import { TokenPayload , TokenResponse } from "./shared-constants" ;
87import { Key } from "../util/KeyUtil" ;
8+ import { logger } from "../util/Logger" ;
99import { createClientAssertion } from "../util/SigningUtil" ;
10- import { generators } from "openid-client " ;
10+ import { TokenPayload , TokenResponse } from "./shared-constants " ;
1111
1212export interface NdiOidcHelperConstructor {
1313 oidcConfigUrl : string ;
@@ -21,6 +21,7 @@ export interface NdiOidcHelperConstructor {
2121interface OidcConfig {
2222 issuer : string ;
2323 authorization_endpoint : string ;
24+ userinfo_endpoint : string ;
2425 token_endpoint : string ;
2526 jwks_uri : string ;
2627}
@@ -117,19 +118,58 @@ export class NdiOidcHelper {
117118 logger . error ( "Failed to get ID token: invalid response data" , response . data ) ;
118119 throw new SingpassMyInfoError ( "Failed to get ID token" ) ;
119120 }
121+ if ( ! response . data . access_token ) {
122+ logger . error ( "Failed to get access token: invalid response data" , response . data ) ;
123+ throw new SingpassMyInfoError ( "Failed to get access token" ) ;
124+ }
120125 return response . data ;
121126 } ;
122127
128+ public getUserInfo = async ( jweResponse : string , overrideDecryptKey ?: Key ) => {
129+ try {
130+ const keys = await this . getKeys ( ) ;
131+
132+ const finalDecryptionKey = overrideDecryptKey ?? this . jweDecryptKey ;
133+ const decryptedJwe = await JweUtil . decryptJWE (
134+ jweResponse ,
135+ finalDecryptionKey . key ,
136+ finalDecryptionKey . format ,
137+ ) ;
138+ const jwsPayload = decryptedJwe . payload . toString ( ) ;
139+ try {
140+ const verified = await JweUtil . verifyJwsUsingKeyStore ( jwsPayload , keys ) ;
141+ return JSON . parse ( verified . payload . toString ( ) ) as TokenPayload ;
142+ } catch ( e ) {
143+ logger . error ( "could not verify user info payload" , e ) ;
144+ throw e ;
145+ }
146+ } catch ( e ) {
147+ logger . error ( "Failed to get user info" , e ) ;
148+ throw e ;
149+ }
150+ } ;
151+
152+ private getKeys = async ( ) => {
153+ const { jwks_uri } = await this . oidcConfig ;
154+ const {
155+ data : { keys } ,
156+ } = await this . axiosClient . get < { keys : object [ ] } > ( jwks_uri ) ;
157+
158+ return keys ;
159+ } ;
160+
161+ // =============================================================================
162+ // Deprecated
163+ // =============================================================================
123164 /**
165+ * @deprecated should not be used with full NDI PKCE flow
166+ *
124167 * Decrypts the ID Token JWT inside the TokenResponse to get the payload
125168 * Use extractNricAndUuidFromPayload on the returned Token Payload to get the NRIC and UUID
126169 */
127170 public async getIdTokenPayload ( tokens : TokenResponse , overrideDecryptKey ?: Key ) : Promise < TokenPayload > {
128171 try {
129- const { jwks_uri } = await this . oidcConfig ;
130- const {
131- data : { keys } ,
132- } = await this . axiosClient . get < { keys : object [ ] } > ( jwks_uri ) ;
172+ const keys = await this . getKeys ( ) ;
133173
134174 const { id_token } = tokens ;
135175
@@ -150,6 +190,8 @@ export class NdiOidcHelper {
150190 }
151191
152192 /**
193+ * @deprecated should not be used with full NDI PKCE flow
194+ *
153195 * Returns the nric and uuid from the token payload
154196 */
155197 public extractNricAndUuidFromPayload ( payload : TokenPayload ) : { nric : string ; uuid : string } {
0 commit comments