@pagopa/io-react-native-wallet 0.13.1 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/commonjs/cie/component.js +180 -0
- package/lib/commonjs/cie/component.js.map +1 -0
- package/lib/commonjs/cie/error.js +44 -0
- package/lib/commonjs/cie/error.js.map +1 -0
- package/lib/commonjs/cie/index.js +32 -0
- package/lib/commonjs/cie/index.js.map +1 -0
- package/lib/commonjs/cie/manager.js +142 -0
- package/lib/commonjs/cie/manager.js.map +1 -0
- package/lib/commonjs/client/index.js +5 -2
- package/lib/commonjs/client/index.js.map +1 -1
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +144 -19
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +12 -4
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/index.js +18 -0
- package/lib/commonjs/credential/issuance/index.js.map +1 -1
- package/lib/commonjs/credential/issuance/types.js +9 -1
- package/lib/commonjs/credential/issuance/types.js.map +1 -1
- package/lib/commonjs/index.js +3 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/trust/types.js +5 -3
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/utils/decoder.js +28 -19
- package/lib/commonjs/utils/decoder.js.map +1 -1
- package/lib/module/cie/component.js +171 -0
- package/lib/module/cie/component.js.map +1 -0
- package/lib/module/cie/error.js +36 -0
- package/lib/module/cie/error.js.map +1 -0
- package/lib/module/cie/index.js +4 -0
- package/lib/module/cie/index.js.map +1 -0
- package/lib/module/cie/manager.js +133 -0
- package/lib/module/cie/manager.js.map +1 -0
- package/lib/module/client/index.js +5 -2
- package/lib/module/client/index.js.map +1 -1
- package/lib/module/credential/issuance/04-complete-user-authorization.js +141 -18
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +12 -4
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/issuance/index.js +2 -2
- package/lib/module/credential/issuance/index.js.map +1 -1
- package/lib/module/credential/issuance/types.js +7 -0
- package/lib/module/credential/issuance/types.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/trust/types.js +5 -3
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/utils/decoder.js +28 -19
- package/lib/module/utils/decoder.js.map +1 -1
- package/lib/typescript/cie/component.d.ts +46 -0
- package/lib/typescript/cie/component.d.ts.map +1 -0
- package/lib/typescript/cie/error.d.ts +31 -0
- package/lib/typescript/cie/error.d.ts.map +1 -0
- package/lib/typescript/cie/index.d.ts +4 -0
- package/lib/typescript/cie/index.d.ts.map +1 -0
- package/lib/typescript/cie/manager.d.ts +5 -0
- package/lib/typescript/cie/manager.d.ts.map +1 -0
- package/lib/typescript/client/index.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +48 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts +1 -0
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/index.d.ts +3 -3
- package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/types.d.ts +10 -0
- package/lib/typescript/credential/issuance/types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/trust/index.d.ts +14 -14
- package/lib/typescript/trust/types.d.ts +142 -142
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/utils/decoder.d.ts.map +1 -1
- package/package.json +6 -2
- package/src/cie/component.tsx +216 -0
- package/src/cie/error.ts +58 -0
- package/src/cie/index.ts +4 -0
- package/src/cie/manager.ts +183 -0
- package/src/client/index.ts +4 -1
- package/src/credential/issuance/04-complete-user-authorization.ts +216 -21
- package/src/credential/issuance/07-verify-and-parse-credential.ts +14 -6
- package/src/credential/issuance/index.ts +10 -0
- package/src/credential/issuance/types.ts +7 -0
- package/src/index.ts +2 -0
- package/src/trust/types.ts +8 -6
- package/src/utils/decoder.ts +28 -19
- package/lib/commonjs/credential/issuance/03-start-credential-issuance.js +0 -287
- package/lib/commonjs/credential/issuance/03-start-credential-issuance.js.map +0 -1
- package/lib/module/credential/issuance/03-start-credential-issuance.js +0 -276
- package/lib/module/credential/issuance/03-start-credential-issuance.js.map +0 -1
- package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts +0 -41
- package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts.map +0 -1
- package/src/credential/issuance/03-start-credential-issuance.ts +0 -407
@@ -1,41 +0,0 @@
|
|
1
|
-
import { type CryptoContext } from "@pagopa/io-react-native-jwt";
|
2
|
-
import { type Out } from "../../utils/misc";
|
3
|
-
import type { StartFlow } from "./01-start-flow";
|
4
|
-
import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
|
5
|
-
import { type AuthorizationContext, type AuthorizationResult } from "../../utils/auth";
|
6
|
-
import { CredentialResponse } from "./types";
|
7
|
-
export type StartCredentialIssuance = (issuerConf: Out<EvaluateIssuerTrust>["issuerConf"], credentialType: Out<StartFlow>["credentialType"], context: {
|
8
|
-
wiaCryptoContext: CryptoContext;
|
9
|
-
credentialCryptoContext: CryptoContext;
|
10
|
-
authorizationContext?: AuthorizationContext;
|
11
|
-
walletInstanceAttestation: string;
|
12
|
-
redirectUri: string;
|
13
|
-
idphint: string;
|
14
|
-
appFetch?: GlobalFetch["fetch"];
|
15
|
-
}) => Promise<CredentialResponse>;
|
16
|
-
/**
|
17
|
-
* Starts the credential issuance flow to obtain a credential from the issuer.
|
18
|
-
* @param issuerConf The Issuer configuration
|
19
|
-
* @param credentialType The type of the credential to be requested
|
20
|
-
* @param context.wiaCryptoContext The context to access the key associated with the Wallet Instance Attestation
|
21
|
-
* @param context.credentialCryptoContext The context to access the key to associat with credential
|
22
|
-
* @param context.walletInstanceAttestation The Wallet Instance Attestation token
|
23
|
-
* @param context.authorizationContext The context to identify the user which will be used to start the authorization. It's needed only when requesting a PersonalIdentificationData credential. The implementantion should open an in-app browser capable of catching the redirectSchema. If not specified, the default browser is used.
|
24
|
-
* @param context.redirectUri The internal URL to which to redirect has passed the in-app browser login phase. If you don't use authorizationContext remember to register this URL as customUrl or deepLink. See https://reactnative.dev/docs/linking
|
25
|
-
* @param context.idphint Unique identifier of the SPID IDP
|
26
|
-
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
27
|
-
* @throws {AuthorizationError} When the response from the authorization response is not parsable
|
28
|
-
* @returns The credential obtained
|
29
|
-
*/
|
30
|
-
export declare const startCredentialIssuance: StartCredentialIssuance;
|
31
|
-
/**
|
32
|
-
* Authorizes the user using the query mode and the authorization context.
|
33
|
-
* @param authzRequestEndpoint The authorization endpoint of the authorization server
|
34
|
-
* @param params The query parameters to be used in the request
|
35
|
-
* @param redirectUri The URL to which the redirect is made is usually a custom URL or deeplink
|
36
|
-
* @param authorizationContext The AuthorizationContext to manage the internal webview. If not specified, the default browser is used
|
37
|
-
* @returns The authrozation result containing the authorization code, state and issuer
|
38
|
-
*/
|
39
|
-
export declare const authorizeUserWithQueryMode: (authzRequestEndpoint: string, params: URLSearchParams, redirectUri: string, authorizationContext?: AuthorizationContext) => Promise<AuthorizationResult>;
|
40
|
-
export declare const createNonceProof: (nonce: string, issuer: string, audience: string, ctx: CryptoContext) => Promise<string>;
|
41
|
-
//# sourceMappingURL=03-start-credential-issuance.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"03-start-credential-issuance.d.ts","sourceRoot":"","sources":["../../../../src/credential/issuance/03-start-credential-issuance.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAIL,KAAK,GAAG,EACT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAQtE,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,kBAAkB,EAAoC,MAAM,SAAS,CAAC;AAuD/E,MAAM,MAAM,uBAAuB,GAAG,CACpC,UAAU,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC,YAAY,CAAC,EAClD,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,gBAAgB,CAAC,EAChD,OAAO,EAAE;IACP,gBAAgB,EAAE,aAAa,CAAC;IAChC,uBAAuB,EAAE,aAAa,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,yBAAyB,EAAE,MAAM,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACjC,KACE,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,uBAAuB,EAAE,uBAgNrC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,yBACf,MAAM,UACpB,eAAe,eACV,MAAM,yBACI,oBAAoB,KAC1C,QAAQ,mBAAmB,CAkD7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,UACpB,MAAM,UACL,MAAM,YACJ,MAAM,OACX,aAAa,KACjB,QAAQ,MAAM,CAehB,CAAC"}
|
@@ -1,407 +0,0 @@
|
|
1
|
-
import uuid from "react-native-uuid";
|
2
|
-
import { AuthorizationDetail, makeParRequest } from "../../utils/par";
|
3
|
-
import { SignJWT, type CryptoContext } from "@pagopa/io-react-native-jwt";
|
4
|
-
import {
|
5
|
-
generateRandomAlphaNumericString,
|
6
|
-
hasStatus,
|
7
|
-
until,
|
8
|
-
type Out,
|
9
|
-
} from "../../utils/misc";
|
10
|
-
import type { StartFlow } from "./01-start-flow";
|
11
|
-
import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
|
12
|
-
import { ASSERTION_TYPE } from "./const";
|
13
|
-
import parseUrl from "parse-url";
|
14
|
-
import {
|
15
|
-
AuthorizationError,
|
16
|
-
AuthorizationIdpError,
|
17
|
-
ValidationFailed,
|
18
|
-
} from "../../utils/errors";
|
19
|
-
import {
|
20
|
-
AuthorizationErrorShape,
|
21
|
-
AuthorizationResultShape,
|
22
|
-
type AuthorizationContext,
|
23
|
-
type AuthorizationResult,
|
24
|
-
} from "../../utils/auth";
|
25
|
-
import { withEphemeralKey } from "../../utils/crypto";
|
26
|
-
import { createDPopToken } from "../../utils/dpop";
|
27
|
-
import { createPopToken } from "../../utils/pop";
|
28
|
-
import { CredentialResponse, TokenResponse, type ResponseMode } from "./types";
|
29
|
-
import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
|
30
|
-
import { Linking } from "react-native";
|
31
|
-
|
32
|
-
/**
|
33
|
-
* Ensures that the credential type requested is supported by the issuer and contained in the
|
34
|
-
* issuer configuration.
|
35
|
-
* @param issuerConf The issuer configuration
|
36
|
-
* @param credentialType The type of the credential to be requested
|
37
|
-
* @returns The credential definition to be used in the request which includes the format and the type and its type
|
38
|
-
*/
|
39
|
-
const selectCredentialDefinition = (
|
40
|
-
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
41
|
-
credentialType: Out<StartFlow>["credentialType"]
|
42
|
-
): AuthorizationDetail => {
|
43
|
-
const credential_configurations_supported =
|
44
|
-
issuerConf.openid_credential_issuer.credential_configurations_supported;
|
45
|
-
|
46
|
-
const [result] = Object.keys(credential_configurations_supported)
|
47
|
-
.filter((e) => e.includes(credentialType))
|
48
|
-
.map((e) => ({
|
49
|
-
credential_configuration_id: credentialType,
|
50
|
-
format: credential_configurations_supported[e]!.format,
|
51
|
-
type: "openid_credential" as const,
|
52
|
-
}));
|
53
|
-
|
54
|
-
if (!result) {
|
55
|
-
throw new Error(`No credential support the type '${credentialType}'`);
|
56
|
-
}
|
57
|
-
return result;
|
58
|
-
};
|
59
|
-
|
60
|
-
/**
|
61
|
-
* Ensures that the response mode requested is supported by the issuer and contained in the issuer configuration.
|
62
|
-
* @param issuerConf The issuer configuration
|
63
|
-
* @param credentialType The type of the credential to be requested
|
64
|
-
* @returns The response mode to be used in the request, "query" for PersonIdentificationData and "form_post.jwt" for all other types.
|
65
|
-
*/
|
66
|
-
const selectResponseMode = (
|
67
|
-
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
68
|
-
credentialType: Out<StartFlow>["credentialType"]
|
69
|
-
): ResponseMode => {
|
70
|
-
const responseModeSupported =
|
71
|
-
issuerConf.oauth_authorization_server.response_modes_supported;
|
72
|
-
|
73
|
-
const responseMode =
|
74
|
-
credentialType === "PersonIdentificationData" ? "query" : "form_post.jwt";
|
75
|
-
|
76
|
-
if (!responseModeSupported.includes(responseMode)) {
|
77
|
-
throw new Error(`No response mode support the type '${credentialType}'`);
|
78
|
-
}
|
79
|
-
|
80
|
-
return responseMode;
|
81
|
-
};
|
82
|
-
|
83
|
-
export type StartCredentialIssuance = (
|
84
|
-
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
85
|
-
credentialType: Out<StartFlow>["credentialType"],
|
86
|
-
context: {
|
87
|
-
wiaCryptoContext: CryptoContext;
|
88
|
-
credentialCryptoContext: CryptoContext;
|
89
|
-
authorizationContext?: AuthorizationContext;
|
90
|
-
walletInstanceAttestation: string;
|
91
|
-
redirectUri: string;
|
92
|
-
idphint: string;
|
93
|
-
appFetch?: GlobalFetch["fetch"];
|
94
|
-
}
|
95
|
-
) => Promise<CredentialResponse>;
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Starts the credential issuance flow to obtain a credential from the issuer.
|
99
|
-
* @param issuerConf The Issuer configuration
|
100
|
-
* @param credentialType The type of the credential to be requested
|
101
|
-
* @param context.wiaCryptoContext The context to access the key associated with the Wallet Instance Attestation
|
102
|
-
* @param context.credentialCryptoContext The context to access the key to associat with credential
|
103
|
-
* @param context.walletInstanceAttestation The Wallet Instance Attestation token
|
104
|
-
* @param context.authorizationContext The context to identify the user which will be used to start the authorization. It's needed only when requesting a PersonalIdentificationData credential. The implementantion should open an in-app browser capable of catching the redirectSchema. If not specified, the default browser is used.
|
105
|
-
* @param context.redirectUri The internal URL to which to redirect has passed the in-app browser login phase. If you don't use authorizationContext remember to register this URL as customUrl or deepLink. See https://reactnative.dev/docs/linking
|
106
|
-
* @param context.idphint Unique identifier of the SPID IDP
|
107
|
-
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
108
|
-
* @throws {AuthorizationError} When the response from the authorization response is not parsable
|
109
|
-
* @returns The credential obtained
|
110
|
-
*/
|
111
|
-
|
112
|
-
export const startCredentialIssuance: StartCredentialIssuance = async (
|
113
|
-
issuerConf,
|
114
|
-
credentialType,
|
115
|
-
ctx
|
116
|
-
) => {
|
117
|
-
const {
|
118
|
-
wiaCryptoContext,
|
119
|
-
credentialCryptoContext,
|
120
|
-
walletInstanceAttestation,
|
121
|
-
authorizationContext,
|
122
|
-
redirectUri,
|
123
|
-
idphint,
|
124
|
-
appFetch = fetch,
|
125
|
-
} = ctx;
|
126
|
-
|
127
|
-
/**
|
128
|
-
* Creates and sends a PAR request to the /as/par endpoint of the authroization server.
|
129
|
-
* This starts the authentication flow to obtain an access token.
|
130
|
-
* This token enables the Wallet Instance to request a digital credential from the Credential Endpoint of the Credential Issuer.
|
131
|
-
* This is an HTTP POST request containing the Wallet Instance identifier (client id), the code challenge and challenge method as specified by PKCE according to RFC 9126
|
132
|
-
* along with the WTE and its proof of possession (WTE-PoP).
|
133
|
-
* Additionally, it includes a request object, which is a signed JWT encapsulating the type of digital credential requested (authorization_details),
|
134
|
-
* the application session identifier on the Wallet Instance side (state),
|
135
|
-
* the method (query or form_post.jwt) by which the Authorization Server
|
136
|
-
* should transmit the Authorization Response containing the authorization code issued upon the end user's authentication (response_mode)
|
137
|
-
* to the Wallet Instance's Token Endpoint to obtain the Access Token, and the redirect_uri of the Wallet Instance where the Authorization Response
|
138
|
-
* should be delivered. The redirect is achived by using a custom URL scheme that the Wallet Instance is registered to handle.
|
139
|
-
*/
|
140
|
-
const clientId = await wiaCryptoContext.getPublicKey().then((_) => _.kid);
|
141
|
-
const codeVerifier = generateRandomAlphaNumericString(64);
|
142
|
-
const parEndpoint =
|
143
|
-
issuerConf.oauth_authorization_server.pushed_authorization_request_endpoint;
|
144
|
-
const parUrl = new URL(parEndpoint);
|
145
|
-
const aud = `${parUrl.protocol}//${parUrl.hostname}`;
|
146
|
-
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
|
147
|
-
.payload.cnf.jwk.kid;
|
148
|
-
const credentialDefinition = selectCredentialDefinition(
|
149
|
-
issuerConf,
|
150
|
-
credentialType
|
151
|
-
);
|
152
|
-
const responseMode = selectResponseMode(issuerConf, credentialType);
|
153
|
-
|
154
|
-
const getPar = makeParRequest({ wiaCryptoContext, appFetch });
|
155
|
-
const issuerRequestUri = await getPar(
|
156
|
-
clientId,
|
157
|
-
codeVerifier,
|
158
|
-
redirectUri,
|
159
|
-
responseMode,
|
160
|
-
parEndpoint,
|
161
|
-
walletInstanceAttestation,
|
162
|
-
[credentialDefinition],
|
163
|
-
ASSERTION_TYPE
|
164
|
-
);
|
165
|
-
|
166
|
-
/**
|
167
|
-
* Starts the authorization flow which dependes on the response mode and the request credential.
|
168
|
-
* If the response mode is "query" the authorization flow is handled differently via the authorization context which opens an in-app browser capable of catching the redirectSchema.
|
169
|
-
* The form_post.jwt mode is not currently supported.
|
170
|
-
*/
|
171
|
-
const authorizeFlowResult = await (async () => {
|
172
|
-
const authzRequestEndpoint =
|
173
|
-
issuerConf.oauth_authorization_server.authorization_endpoint;
|
174
|
-
if (responseMode === "query") {
|
175
|
-
const params = new URLSearchParams({
|
176
|
-
client_id: clientId,
|
177
|
-
request_uri: issuerRequestUri,
|
178
|
-
idphint,
|
179
|
-
});
|
180
|
-
|
181
|
-
/**
|
182
|
-
* Starts the authorization flow to obtain an authorization code by performing a GET request to the /authorize endpoint of the authorization server.
|
183
|
-
*/
|
184
|
-
return await authorizeUserWithQueryMode(
|
185
|
-
authzRequestEndpoint,
|
186
|
-
params,
|
187
|
-
redirectUri,
|
188
|
-
authorizationContext
|
189
|
-
);
|
190
|
-
} else {
|
191
|
-
throw new AuthorizationError(
|
192
|
-
"Response mode not supported for this type of credential"
|
193
|
-
);
|
194
|
-
}
|
195
|
-
})();
|
196
|
-
|
197
|
-
/**
|
198
|
-
* Creates and sends the DPoP Proof JWT to be presented with the authorization code to the /token endpoint of the authorization server
|
199
|
-
* for requesting the issuance of an access token bound to the public key of the Wallet Instance contained within the DPoP.
|
200
|
-
* This enables the Wallet Instance to request a digital credential.
|
201
|
-
* The DPoP Proof JWT is generated according to the section 4.3 of the DPoP RFC 9449 specification.
|
202
|
-
*/
|
203
|
-
|
204
|
-
const { code } = authorizeFlowResult;
|
205
|
-
const tokenUrl = issuerConf.oauth_authorization_server.token_endpoint;
|
206
|
-
// Use an ephemeral key to be destroyed after use
|
207
|
-
const tokenRequestSignedDPop = await withEphemeralKey(
|
208
|
-
async (ephimeralContext) => {
|
209
|
-
return await createDPopToken(
|
210
|
-
{
|
211
|
-
htm: "POST",
|
212
|
-
htu: tokenUrl,
|
213
|
-
jti: `${uuid.v4()}`,
|
214
|
-
},
|
215
|
-
ephimeralContext
|
216
|
-
);
|
217
|
-
}
|
218
|
-
);
|
219
|
-
|
220
|
-
const signedWiaPoP = await createPopToken(
|
221
|
-
{
|
222
|
-
jti: `${uuid.v4()}`,
|
223
|
-
aud,
|
224
|
-
iss,
|
225
|
-
},
|
226
|
-
wiaCryptoContext
|
227
|
-
);
|
228
|
-
|
229
|
-
const requestBody = {
|
230
|
-
grant_type: "authorization_code",
|
231
|
-
client_id: clientId,
|
232
|
-
code,
|
233
|
-
redirect_uri: redirectUri,
|
234
|
-
code_verifier: codeVerifier,
|
235
|
-
client_assertion_type: ASSERTION_TYPE,
|
236
|
-
client_assertion: walletInstanceAttestation + "~" + signedWiaPoP,
|
237
|
-
};
|
238
|
-
|
239
|
-
const authorizationRequestFormBody = new URLSearchParams(requestBody);
|
240
|
-
const tokenRes = await appFetch(tokenUrl, {
|
241
|
-
method: "POST",
|
242
|
-
headers: {
|
243
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
244
|
-
DPoP: tokenRequestSignedDPop,
|
245
|
-
},
|
246
|
-
body: authorizationRequestFormBody.toString(),
|
247
|
-
})
|
248
|
-
.then(hasStatus(200))
|
249
|
-
.then((res) => res.json())
|
250
|
-
.then((body) => TokenResponse.safeParse(body));
|
251
|
-
|
252
|
-
if (!tokenRes.success) {
|
253
|
-
throw new ValidationFailed(tokenRes.error.message);
|
254
|
-
}
|
255
|
-
|
256
|
-
/**
|
257
|
-
* Validates the token response and extracts the access token, c_nonce and c_nonce_expires_in.
|
258
|
-
*/
|
259
|
-
const accessTokenResponse = tokenRes.data;
|
260
|
-
const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
|
261
|
-
|
262
|
-
/**
|
263
|
-
* JWT proof token to bind the request nonce to the key that will bind the holder User with the Credential
|
264
|
-
* This is presented along with the access token to the Credential Endpoint as proof of possession of the private key used to sign the Access Token.
|
265
|
-
* @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-proof-types
|
266
|
-
*/
|
267
|
-
const signedNonceProof = await createNonceProof(
|
268
|
-
accessTokenResponse.c_nonce,
|
269
|
-
clientId,
|
270
|
-
credentialUrl,
|
271
|
-
credentialCryptoContext
|
272
|
-
);
|
273
|
-
|
274
|
-
// Validation of accessTokenResponse.authorization_details if contain credentialDefinition
|
275
|
-
const constainsCredentialDefinition =
|
276
|
-
accessTokenResponse.authorization_details.some(
|
277
|
-
(c) =>
|
278
|
-
c.credential_configuration_id ===
|
279
|
-
credentialDefinition.credential_configuration_id &&
|
280
|
-
c.format === credentialDefinition.format &&
|
281
|
-
c.type === credentialDefinition.type
|
282
|
-
);
|
283
|
-
|
284
|
-
if (!constainsCredentialDefinition) {
|
285
|
-
throw new ValidationFailed(
|
286
|
-
"The access token response does not contain the requested credential"
|
287
|
-
);
|
288
|
-
}
|
289
|
-
|
290
|
-
/** The credential request body */
|
291
|
-
const credentialRequestFormBody = {
|
292
|
-
credential_definition: {
|
293
|
-
type: [credentialDefinition.credential_configuration_id],
|
294
|
-
},
|
295
|
-
format: credentialDefinition.format,
|
296
|
-
proof: {
|
297
|
-
jwt: signedNonceProof,
|
298
|
-
proof_type: "jwt",
|
299
|
-
},
|
300
|
-
};
|
301
|
-
|
302
|
-
const credentialRes = await appFetch(credentialUrl, {
|
303
|
-
method: "POST",
|
304
|
-
headers: {
|
305
|
-
"Content-Type": "application/json",
|
306
|
-
DPoP: tokenRequestSignedDPop,
|
307
|
-
Authorization: `${accessTokenResponse.token_type} ${accessTokenResponse.access_token}`,
|
308
|
-
},
|
309
|
-
body: JSON.stringify(credentialRequestFormBody),
|
310
|
-
})
|
311
|
-
.then(hasStatus(200))
|
312
|
-
.then((res) => res.json())
|
313
|
-
.then((body) => CredentialResponse.safeParse(body));
|
314
|
-
|
315
|
-
if (!credentialRes.success) {
|
316
|
-
throw new ValidationFailed(credentialRes.error.message);
|
317
|
-
}
|
318
|
-
|
319
|
-
return credentialRes.data;
|
320
|
-
};
|
321
|
-
|
322
|
-
/**
|
323
|
-
* Authorizes the user using the query mode and the authorization context.
|
324
|
-
* @param authzRequestEndpoint The authorization endpoint of the authorization server
|
325
|
-
* @param params The query parameters to be used in the request
|
326
|
-
* @param redirectUri The URL to which the redirect is made is usually a custom URL or deeplink
|
327
|
-
* @param authorizationContext The AuthorizationContext to manage the internal webview. If not specified, the default browser is used
|
328
|
-
* @returns The authrozation result containing the authorization code, state and issuer
|
329
|
-
*/
|
330
|
-
export const authorizeUserWithQueryMode = async (
|
331
|
-
authzRequestEndpoint: string,
|
332
|
-
params: URLSearchParams,
|
333
|
-
redirectUri: string,
|
334
|
-
authorizationContext?: AuthorizationContext
|
335
|
-
): Promise<AuthorizationResult> => {
|
336
|
-
const authUrl = `${authzRequestEndpoint}?${params}`;
|
337
|
-
var authRedirectUrl: string | undefined;
|
338
|
-
|
339
|
-
if (authorizationContext) {
|
340
|
-
const redirectSchema = new URL(redirectUri).protocol.replace(":", "");
|
341
|
-
authRedirectUrl = await authorizationContext
|
342
|
-
.authorize(authUrl, redirectSchema)
|
343
|
-
.catch((e) => {
|
344
|
-
throw new AuthorizationError(e.message);
|
345
|
-
});
|
346
|
-
} else {
|
347
|
-
// handler for redirectUri
|
348
|
-
Linking.addEventListener("url", ({ url }) => {
|
349
|
-
if (url.includes(redirectUri)) {
|
350
|
-
authRedirectUrl = url;
|
351
|
-
}
|
352
|
-
});
|
353
|
-
|
354
|
-
const openAuthUrlInBrowser = Linking.openURL(authUrl);
|
355
|
-
|
356
|
-
/*
|
357
|
-
* Waits for 120 seconds for the identificationRedirectUrl variable to be set
|
358
|
-
* by the custom url handler. If the timeout is exceeded, throw an exception
|
359
|
-
*/
|
360
|
-
const unitAuthRedirectIsNotUndefined = until(
|
361
|
-
() => authRedirectUrl !== undefined,
|
362
|
-
120
|
363
|
-
);
|
364
|
-
|
365
|
-
await Promise.all([openAuthUrlInBrowser, unitAuthRedirectIsNotUndefined]);
|
366
|
-
|
367
|
-
if (authRedirectUrl === undefined) {
|
368
|
-
throw new AuthorizationError("Invalid authentication redirect url");
|
369
|
-
}
|
370
|
-
}
|
371
|
-
|
372
|
-
const urlParse = parseUrl(authRedirectUrl);
|
373
|
-
const authRes = AuthorizationResultShape.safeParse(urlParse.query);
|
374
|
-
if (!authRes.success) {
|
375
|
-
const authErr = AuthorizationErrorShape.safeParse(urlParse.query);
|
376
|
-
if (!authErr.success) {
|
377
|
-
throw new AuthorizationError(authRes.error.message); // an error occured while parsing the result and the error
|
378
|
-
}
|
379
|
-
throw new AuthorizationIdpError(
|
380
|
-
authErr.data.error,
|
381
|
-
authErr.data.error_description
|
382
|
-
);
|
383
|
-
}
|
384
|
-
return authRes.data;
|
385
|
-
};
|
386
|
-
|
387
|
-
export const createNonceProof = async (
|
388
|
-
nonce: string,
|
389
|
-
issuer: string,
|
390
|
-
audience: string,
|
391
|
-
ctx: CryptoContext
|
392
|
-
): Promise<string> => {
|
393
|
-
const jwk = await ctx.getPublicKey();
|
394
|
-
return new SignJWT(ctx)
|
395
|
-
.setPayload({
|
396
|
-
nonce,
|
397
|
-
})
|
398
|
-
.setProtectedHeader({
|
399
|
-
typ: "openid4vci-proof+jwt",
|
400
|
-
jwk,
|
401
|
-
})
|
402
|
-
.setAudience(audience)
|
403
|
-
.setIssuer(issuer)
|
404
|
-
.setIssuedAt()
|
405
|
-
.setExpirationTime("5min")
|
406
|
-
.sign();
|
407
|
-
};
|