oauth4webapi 1.2.2 → 1.3.0
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.
- package/README.md +12 -37
- package/build/index.d.ts +38 -5
- package/build/index.js +25 -67
- package/package.json +15 -11
package/README.md
CHANGED
|
@@ -52,26 +52,18 @@ import * as oauth2 from 'https://deno.land/x/oauth4webapi/src/index.ts'
|
|
|
52
52
|
- FAPI 2.0 (Private Key JWT, PAR, DPoP) - [source](examples/fapi2.ts)
|
|
53
53
|
- FAPI 2.0 Message Signing (Private Key JWT, PAR, DPoP, JAR, JARM) - [source](examples/fapi2-message-signing.ts) | [diff](examples/fapi2-message-signing.diff)
|
|
54
54
|
|
|
55
|
-
##
|
|
56
|
-
|
|
57
|
-
The supported JavaScript runtimes include ones that
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
- These are (not an exhaustive list):
|
|
68
|
-
- Browsers
|
|
69
|
-
- Cloudflare Workers
|
|
70
|
-
- Deno
|
|
71
|
-
- Electron
|
|
72
|
-
- Next.js Middlewares
|
|
73
|
-
- Node.js ([runtime flags may be needed](https://github.com/panva/oauth4webapi/issues/8))
|
|
74
|
-
- Vercel Edge Functions
|
|
55
|
+
## Supported Runtimes
|
|
56
|
+
|
|
57
|
+
The supported JavaScript runtimes include ones that support the utilized Web API globals and standard built-in objects
|
|
58
|
+
|
|
59
|
+
These are _(this is not an exhaustive list)_:
|
|
60
|
+
- Browsers
|
|
61
|
+
- Cloudflare Workers
|
|
62
|
+
- Deno
|
|
63
|
+
- Electron
|
|
64
|
+
- Netlify Edge
|
|
65
|
+
- Node.js ([runtime flags may be needed](https://github.com/panva/oauth4webapi/issues/8))
|
|
66
|
+
- Vercel's Edge Runtime
|
|
75
67
|
|
|
76
68
|
## Out of scope
|
|
77
69
|
|
|
@@ -81,20 +73,3 @@ The supported JavaScript runtimes include ones that
|
|
|
81
73
|
- JSON Web Encryption (JWE)
|
|
82
74
|
- JSON Web Signature (JWS) rarely used algorithms and HMAC
|
|
83
75
|
- Automatic polyfills of any kind
|
|
84
|
-
|
|
85
|
-
[web crypto api]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
|
|
86
|
-
[fetch api]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
|
87
|
-
[fetch]: https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
|
88
|
-
[textdecoder]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
|
|
89
|
-
[textencoder]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
|
|
90
|
-
[btoa]: https://developer.mozilla.org/en-US/docs/Web/API/btoa
|
|
91
|
-
[atob]: https://developer.mozilla.org/en-US/docs/Web/API/atob
|
|
92
|
-
[uint8array]: https://developer.mozilla.org/en-US/docs/Web/API/Uint8Array
|
|
93
|
-
[response]: https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
94
|
-
[headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
|
95
|
-
[crypto]: https://developer.mozilla.org/en-US/docs/Web/API/crypto
|
|
96
|
-
[cryptokey]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
|
|
97
|
-
[urlsearchparams]: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
|
|
98
|
-
[encoding api]: https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API
|
|
99
|
-
[url api]: https://developer.mozilla.org/en-US/docs/Web/API/URL_API
|
|
100
|
-
[url]: https://developer.mozilla.org/en-US/docs/Web/API/URL
|
package/build/index.d.ts
CHANGED
|
@@ -703,6 +703,31 @@ export interface UserInfoResponse {
|
|
|
703
703
|
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)
|
|
704
704
|
*/
|
|
705
705
|
export declare const skipSubjectCheck: unique symbol;
|
|
706
|
+
export interface SkipJWTSignatureCheckOptions {
|
|
707
|
+
/**
|
|
708
|
+
* DANGER ZONE
|
|
709
|
+
*
|
|
710
|
+
* When JWT assertions are received via direct communication between the Client and the
|
|
711
|
+
* Token/UserInfo/Introspection endpoint (which they are in this library's supported profiles and
|
|
712
|
+
* exposed functions) the TLS server validation MAY be used to validate the issuer in place of
|
|
713
|
+
* checking the assertion's signature.
|
|
714
|
+
*
|
|
715
|
+
* Set this to `true` to omit verifying the JWT assertion's signature (e.g. ID Token, JWT Signed
|
|
716
|
+
* Introspection, or JWT Signed UserInfo Response).
|
|
717
|
+
*
|
|
718
|
+
* Setting this to `true` also means that:
|
|
719
|
+
*
|
|
720
|
+
* - The Authorization Server's JSON Web Key Set will not be requested. That is useful for
|
|
721
|
+
* javascript runtimes that execute on the edge and cannot reliably share an in-memory cache of
|
|
722
|
+
* the JSON Web Key Set in between invocations.
|
|
723
|
+
* - Any JWS Algorithm may be used, not just the {@link JWSAlgorithm supported ones}.
|
|
724
|
+
*
|
|
725
|
+
* Default is `false`.
|
|
726
|
+
*/
|
|
727
|
+
skipJwtSignatureCheck?: boolean;
|
|
728
|
+
}
|
|
729
|
+
export interface ProcessUserInfoResponseOptions extends HttpRequestOptions, SkipJWTSignatureCheckOptions {
|
|
730
|
+
}
|
|
706
731
|
/**
|
|
707
732
|
* Validates Response instance to be one coming from the
|
|
708
733
|
* {@link AuthorizationServer.userinfo_endpoint `as.userinfo_endpoint`}.
|
|
@@ -719,7 +744,7 @@ export declare const skipSubjectCheck: unique symbol;
|
|
|
719
744
|
* OAuth 2.0 error was returned.
|
|
720
745
|
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)
|
|
721
746
|
*/
|
|
722
|
-
export declare function processUserInfoResponse(as: AuthorizationServer, client: Client, expectedSubject: string | typeof skipSubjectCheck, response: Response, options?:
|
|
747
|
+
export declare function processUserInfoResponse(as: AuthorizationServer, client: Client, expectedSubject: string | typeof skipSubjectCheck, response: Response, options?: ProcessUserInfoResponseOptions): Promise<UserInfoResponse>;
|
|
723
748
|
export interface TokenEndpointRequestOptions extends HttpRequestOptions, AuthenticatedRequestOptions, DPoPRequestOptions {
|
|
724
749
|
/** Any additional parameters to send. This cannot override existing parameter values. */
|
|
725
750
|
additionalParameters?: URLSearchParams;
|
|
@@ -755,6 +780,8 @@ export declare function getValidatedIdTokenClaims(ref: OpenIDTokenEndpointRespon
|
|
|
755
780
|
* @returns JWT Claims Set from an ID Token, or undefined if there is no ID Token in `ref`.
|
|
756
781
|
*/
|
|
757
782
|
export declare function getValidatedIdTokenClaims(ref: TokenEndpointResponse): IDToken | undefined;
|
|
783
|
+
export interface ProcessRefreshTokenResponseOptions extends HttpRequestOptions, SkipJWTSignatureCheckOptions {
|
|
784
|
+
}
|
|
758
785
|
/**
|
|
759
786
|
* Validates Refresh Token Grant Response instance to be one coming from the
|
|
760
787
|
* {@link AuthorizationServer.token_endpoint `as.token_endpoint`}.
|
|
@@ -769,7 +796,7 @@ export declare function getValidatedIdTokenClaims(ref: TokenEndpointResponse): I
|
|
|
769
796
|
* @see [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-6)
|
|
770
797
|
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens)
|
|
771
798
|
*/
|
|
772
|
-
export declare function processRefreshTokenResponse(as: AuthorizationServer, client: Client, response: Response, options?:
|
|
799
|
+
export declare function processRefreshTokenResponse(as: AuthorizationServer, client: Client, response: Response, options?: ProcessRefreshTokenResponseOptions): Promise<TokenEndpointResponse | OAuth2Error>;
|
|
773
800
|
/**
|
|
774
801
|
* Performs an Authorization Code grant request at the
|
|
775
802
|
* {@link AuthorizationServer.token_endpoint `as.token_endpoint`}.
|
|
@@ -856,6 +883,8 @@ export declare const expectNoNonce: unique symbol;
|
|
|
856
883
|
* indicate no `auth_time` ID Token claim value check should be performed.
|
|
857
884
|
*/
|
|
858
885
|
export declare const skipAuthTimeCheck: unique symbol;
|
|
886
|
+
export interface ProcessAuthorizationCodeOpenIDResponseOptions extends HttpRequestOptions, SkipJWTSignatureCheckOptions {
|
|
887
|
+
}
|
|
859
888
|
/**
|
|
860
889
|
* (OpenID Connect only) Validates Authorization Code Grant Response instance to be one coming from
|
|
861
890
|
* the {@link AuthorizationServer.token_endpoint `as.token_endpoint`}.
|
|
@@ -876,7 +905,7 @@ export declare const skipAuthTimeCheck: unique symbol;
|
|
|
876
905
|
* @see [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1)
|
|
877
906
|
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth)
|
|
878
907
|
*/
|
|
879
|
-
export declare function processAuthorizationCodeOpenIDResponse(as: AuthorizationServer, client: Client, response: Response, expectedNonce?: string | typeof expectNoNonce, maxAge?: number | typeof skipAuthTimeCheck, options?:
|
|
908
|
+
export declare function processAuthorizationCodeOpenIDResponse(as: AuthorizationServer, client: Client, response: Response, expectedNonce?: string | typeof expectNoNonce, maxAge?: number | typeof skipAuthTimeCheck, options?: ProcessAuthorizationCodeOpenIDResponseOptions): Promise<OpenIDTokenEndpointResponse | OAuth2Error>;
|
|
880
909
|
/**
|
|
881
910
|
* (OAuth 2.0 without OpenID Connect only) Validates Authorization Code Grant Response instance to
|
|
882
911
|
* be one coming from the {@link AuthorizationServer.token_endpoint `as.token_endpoint`}.
|
|
@@ -991,6 +1020,8 @@ export interface IntrospectionResponse {
|
|
|
991
1020
|
};
|
|
992
1021
|
readonly [claim: string]: JsonValue | undefined;
|
|
993
1022
|
}
|
|
1023
|
+
export interface ProcessIntrospectionResponseOptions extends HttpRequestOptions, SkipJWTSignatureCheckOptions {
|
|
1024
|
+
}
|
|
994
1025
|
/**
|
|
995
1026
|
* Validates Response instance to be one coming from the
|
|
996
1027
|
* {@link AuthorizationServer.introspection_endpoint `as.introspection_endpoint`}.
|
|
@@ -1005,7 +1036,7 @@ export interface IntrospectionResponse {
|
|
|
1005
1036
|
* @see [RFC 7662 - OAuth 2.0 Token Introspection](https://www.rfc-editor.org/rfc/rfc7662.html#section-2)
|
|
1006
1037
|
* @see [draft-ietf-oauth-jwt-introspection-response-12 - JWT Response for OAuth Token Introspection](https://www.ietf.org/archive/id/draft-ietf-oauth-jwt-introspection-response-12.html#section-5)
|
|
1007
1038
|
*/
|
|
1008
|
-
export declare function processIntrospectionResponse(as: AuthorizationServer, client: Client, response: Response, options?:
|
|
1039
|
+
export declare function processIntrospectionResponse(as: AuthorizationServer, client: Client, response: Response, options?: ProcessIntrospectionResponseOptions): Promise<IntrospectionResponse | OAuth2Error>;
|
|
1009
1040
|
/** @ignore */
|
|
1010
1041
|
export interface JwksRequestOptions extends HttpRequestOptions {
|
|
1011
1042
|
}
|
|
@@ -1140,6 +1171,8 @@ export declare function processDeviceAuthorizationResponse(as: AuthorizationServ
|
|
|
1140
1171
|
* @see [draft-ietf-oauth-dpop-10 - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)](https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-10.html#name-dpop-access-token-request)
|
|
1141
1172
|
*/
|
|
1142
1173
|
export declare function deviceCodeGrantRequest(as: AuthorizationServer, client: Client, deviceCode: string, options?: TokenEndpointRequestOptions): Promise<Response>;
|
|
1174
|
+
export interface ProcessDeviceCodeResponseOptions extends HttpRequestOptions, SkipJWTSignatureCheckOptions {
|
|
1175
|
+
}
|
|
1143
1176
|
/**
|
|
1144
1177
|
* Validates Device Authorization Grant Response instance to be one coming from the
|
|
1145
1178
|
* {@link AuthorizationServer.token_endpoint `as.token_endpoint`}.
|
|
@@ -1153,7 +1186,7 @@ export declare function deviceCodeGrantRequest(as: AuthorizationServer, client:
|
|
|
1153
1186
|
* OAuth 2.0 error was returned.
|
|
1154
1187
|
* @see [RFC 8628 - OAuth 2.0 Device Authorization Grant](https://www.rfc-editor.org/rfc/rfc8628.html#section-3.4)
|
|
1155
1188
|
*/
|
|
1156
|
-
export declare function processDeviceCodeResponse(as: AuthorizationServer, client: Client, response: Response, options?:
|
|
1189
|
+
export declare function processDeviceCodeResponse(as: AuthorizationServer, client: Client, response: Response, options?: ProcessDeviceCodeResponseOptions): Promise<TokenEndpointResponse | OAuth2Error>;
|
|
1157
1190
|
export interface GenerateKeyPairOptions {
|
|
1158
1191
|
/** Indicates whether or not the private key may be exported. Default is `false`. */
|
|
1159
1192
|
extractable?: boolean;
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
let USER_AGENT;
|
|
2
2
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
3
3
|
const NAME = 'oauth4webapi';
|
|
4
|
-
const VERSION = 'v1.
|
|
4
|
+
const VERSION = 'v1.3.0';
|
|
5
5
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
6
6
|
}
|
|
7
7
|
const encoder = new TextEncoder();
|
|
@@ -732,10 +732,9 @@ export async function processUserInfoResponse(as, client, expectedSubject, respo
|
|
|
732
732
|
}
|
|
733
733
|
let json;
|
|
734
734
|
if (getContentType(response) === 'application/jwt') {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const { claims } = await validateJwt(await preserveBodyStream(response).text(), checkSigningAlgorithm.bind(undefined, client.userinfo_signed_response_alg, as.userinfo_signing_alg_values_supported), getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options))
|
|
735
|
+
const { claims } = await validateJwt(await preserveBodyStream(response).text(), checkSigningAlgorithm.bind(undefined, client.userinfo_signed_response_alg, as.userinfo_signing_alg_values_supported), options?.skipJwtSignatureCheck !== true
|
|
736
|
+
? getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options)
|
|
737
|
+
: noSignatureCheck)
|
|
739
738
|
.then(validateOptionalAudience.bind(undefined, client.client_id))
|
|
740
739
|
.then(validateOptionalIssuer.bind(undefined, as.issuer));
|
|
741
740
|
json = claims;
|
|
@@ -770,43 +769,6 @@ export async function processUserInfoResponse(as, client, expectedSubject, respo
|
|
|
770
769
|
}
|
|
771
770
|
return json;
|
|
772
771
|
}
|
|
773
|
-
function padded(buf, length) {
|
|
774
|
-
const out = new Uint8Array(length);
|
|
775
|
-
out.set(buf);
|
|
776
|
-
return out;
|
|
777
|
-
}
|
|
778
|
-
function timingSafeEqual(a, b) {
|
|
779
|
-
const len = Math.max(a.byteLength, b.byteLength);
|
|
780
|
-
a = padded(a, len);
|
|
781
|
-
b = padded(b, len);
|
|
782
|
-
let out = 0;
|
|
783
|
-
let i = -1;
|
|
784
|
-
while (++i < len) {
|
|
785
|
-
out |= a[i] ^ b[i];
|
|
786
|
-
}
|
|
787
|
-
return out === 0;
|
|
788
|
-
}
|
|
789
|
-
async function idTokenHash(alg, data) {
|
|
790
|
-
let algorithm;
|
|
791
|
-
switch (alg) {
|
|
792
|
-
case 'RS256':
|
|
793
|
-
case 'PS256':
|
|
794
|
-
case 'ES256':
|
|
795
|
-
algorithm = { name: 'SHA-256' };
|
|
796
|
-
break;
|
|
797
|
-
case 'EdDSA':
|
|
798
|
-
algorithm = { name: 'SHA-512' };
|
|
799
|
-
break;
|
|
800
|
-
default:
|
|
801
|
-
throw new UnsupportedOperationError();
|
|
802
|
-
}
|
|
803
|
-
const digest = await crypto.subtle.digest(algorithm, buf(data));
|
|
804
|
-
return b64u(digest.slice(0, digest.byteLength / 2));
|
|
805
|
-
}
|
|
806
|
-
async function idTokenHashMatches(alg, data, actual) {
|
|
807
|
-
const expected = await idTokenHash(alg, data);
|
|
808
|
-
return timingSafeEqual(buf(actual), buf(expected));
|
|
809
|
-
}
|
|
810
772
|
async function authenticatedRequest(as, client, method, url, body, headers, options) {
|
|
811
773
|
await clientAuthentication(as, client, body, headers, options?.clientPrivateKey);
|
|
812
774
|
return fetch(url.href, {
|
|
@@ -847,7 +809,7 @@ export function getValidatedIdTokenClaims(ref) {
|
|
|
847
809
|
}
|
|
848
810
|
return idTokenClaims.get(ref);
|
|
849
811
|
}
|
|
850
|
-
async function processGenericAccessTokenResponse(as, client, response, options, ignoreIdToken = false, ignoreRefreshToken = false) {
|
|
812
|
+
async function processGenericAccessTokenResponse(as, client, response, options, ignoreIdToken = false, ignoreRefreshToken = false, skipSignatureCheck = false) {
|
|
851
813
|
assertAs(as);
|
|
852
814
|
assertClient(client);
|
|
853
815
|
if (!(response instanceof Response)) {
|
|
@@ -897,10 +859,9 @@ async function processGenericAccessTokenResponse(as, client, response, options,
|
|
|
897
859
|
throw new OPE('"response" body "id_token" property must be a non-empty string');
|
|
898
860
|
}
|
|
899
861
|
if (json.id_token) {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
const { header, claims } = await validateJwt(json.id_token, checkSigningAlgorithm.bind(undefined, client.id_token_signed_response_alg, as.id_token_signing_alg_values_supported), getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options))
|
|
862
|
+
const { claims } = await validateJwt(json.id_token, checkSigningAlgorithm.bind(undefined, client.id_token_signed_response_alg, as.id_token_signing_alg_values_supported), skipSignatureCheck !== true
|
|
863
|
+
? getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options)
|
|
864
|
+
: noSignatureCheck)
|
|
904
865
|
.then(validatePresence.bind(undefined, ['aud', 'exp', 'iat', 'iss', 'sub']))
|
|
905
866
|
.then(validateIssuer.bind(undefined, as.issuer))
|
|
906
867
|
.then(validateAudience.bind(undefined, client.client_id));
|
|
@@ -910,19 +871,13 @@ async function processGenericAccessTokenResponse(as, client, response, options,
|
|
|
910
871
|
if (client.require_auth_time && typeof claims.auth_time !== 'number') {
|
|
911
872
|
throw new OPE('unexpected ID Token "auth_time" (authentication time) claim value');
|
|
912
873
|
}
|
|
913
|
-
if (claims.at_hash !== undefined) {
|
|
914
|
-
if (typeof claims.at_hash !== 'string' ||
|
|
915
|
-
!(await idTokenHashMatches(header.alg, json.access_token, claims.at_hash))) {
|
|
916
|
-
throw new OPE('unexpected ID Token "at_hash" (access token hash) claim value');
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
874
|
idTokenClaims.set(json, claims);
|
|
920
875
|
}
|
|
921
876
|
}
|
|
922
877
|
return json;
|
|
923
878
|
}
|
|
924
879
|
export async function processRefreshTokenResponse(as, client, response, options) {
|
|
925
|
-
return processGenericAccessTokenResponse(as, client, response, options);
|
|
880
|
+
return processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
926
881
|
}
|
|
927
882
|
function validateOptionalAudience(expected, result) {
|
|
928
883
|
if (result.claims.aud !== undefined) {
|
|
@@ -993,7 +948,7 @@ function validatePresence(required, result) {
|
|
|
993
948
|
export const expectNoNonce = Symbol();
|
|
994
949
|
export const skipAuthTimeCheck = Symbol();
|
|
995
950
|
export async function processAuthorizationCodeOpenIDResponse(as, client, response, expectedNonce, maxAge, options) {
|
|
996
|
-
const result = await processGenericAccessTokenResponse(as, client, response, options);
|
|
951
|
+
const result = await processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
997
952
|
if (isOAuth2Error(result)) {
|
|
998
953
|
return result;
|
|
999
954
|
}
|
|
@@ -1137,10 +1092,9 @@ export async function processIntrospectionResponse(as, client, response, options
|
|
|
1137
1092
|
}
|
|
1138
1093
|
let json;
|
|
1139
1094
|
if (getContentType(response) === 'application/token-introspection+jwt') {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const { claims } = await validateJwt(await preserveBodyStream(response).text(), checkSigningAlgorithm.bind(undefined, client.introspection_signed_response_alg, as.introspection_signing_alg_values_supported), getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options))
|
|
1095
|
+
const { claims } = await validateJwt(await preserveBodyStream(response).text(), checkSigningAlgorithm.bind(undefined, client.introspection_signed_response_alg, as.introspection_signing_alg_values_supported), options?.skipJwtSignatureCheck !== true
|
|
1096
|
+
? getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options)
|
|
1097
|
+
: noSignatureCheck)
|
|
1144
1098
|
.then(checkJwtType.bind(undefined, 'token-introspection+jwt'))
|
|
1145
1099
|
.then(validatePresence.bind(undefined, ['aud', 'iat', 'iss']))
|
|
1146
1100
|
.then(validateIssuer.bind(undefined, as.issuer))
|
|
@@ -1260,8 +1214,9 @@ function subtleAlgorithm(key) {
|
|
|
1260
1214
|
}
|
|
1261
1215
|
throw new UnsupportedOperationError();
|
|
1262
1216
|
}
|
|
1217
|
+
const noSignatureCheck = Symbol();
|
|
1263
1218
|
async function validateJwt(jws, checkAlg, getKey) {
|
|
1264
|
-
const { 0: protectedHeader, 1: payload, 2:
|
|
1219
|
+
const { 0: protectedHeader, 1: payload, 2: encodedSignature, length } = jws.split('.');
|
|
1265
1220
|
if (length === 5) {
|
|
1266
1221
|
throw new UnsupportedOperationError('JWE structure JWTs are not supported');
|
|
1267
1222
|
}
|
|
@@ -1282,11 +1237,14 @@ async function validateJwt(jws, checkAlg, getKey) {
|
|
|
1282
1237
|
if (header.crit !== undefined) {
|
|
1283
1238
|
throw new OPE('unexpected JWT "crit" header parameter');
|
|
1284
1239
|
}
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1240
|
+
const signature = b64u(encodedSignature);
|
|
1241
|
+
if (getKey !== noSignatureCheck) {
|
|
1242
|
+
const key = await getKey(header);
|
|
1243
|
+
const input = `${protectedHeader}.${payload}`;
|
|
1244
|
+
const verified = await crypto.subtle.verify(subtleAlgorithm(key), key, signature, buf(input));
|
|
1245
|
+
if (!verified) {
|
|
1246
|
+
throw new OPE('JWT signature verification failed');
|
|
1247
|
+
}
|
|
1290
1248
|
}
|
|
1291
1249
|
let claims;
|
|
1292
1250
|
try {
|
|
@@ -1331,7 +1289,7 @@ async function validateJwt(jws, checkAlg, getKey) {
|
|
|
1331
1289
|
throw new OPE('unexpected JWT "aud" (audience) claim type');
|
|
1332
1290
|
}
|
|
1333
1291
|
}
|
|
1334
|
-
return { header, claims };
|
|
1292
|
+
return { header, claims, signature };
|
|
1335
1293
|
}
|
|
1336
1294
|
export async function validateJwtAuthResponse(as, client, parameters, expectedState, options) {
|
|
1337
1295
|
assertAs(as);
|
|
@@ -1536,7 +1494,7 @@ export async function deviceCodeGrantRequest(as, client, deviceCode, options) {
|
|
|
1536
1494
|
return tokenEndpointRequest(as, client, 'urn:ietf:params:oauth:grant-type:device_code', parameters, options);
|
|
1537
1495
|
}
|
|
1538
1496
|
export async function processDeviceCodeResponse(as, client, response, options) {
|
|
1539
|
-
return processGenericAccessTokenResponse(as, client, response, options);
|
|
1497
|
+
return processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
1540
1498
|
}
|
|
1541
1499
|
export async function generateKeyPair(alg, options) {
|
|
1542
1500
|
let algorithm;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oauth4webapi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "OAuth 2 / OpenID Connect for Web Platform API JavaScript runtimes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
"browser",
|
|
11
11
|
"certified",
|
|
12
12
|
"client",
|
|
13
|
-
"cloudflare
|
|
13
|
+
"cloudflare",
|
|
14
14
|
"deno",
|
|
15
|
+
"edge",
|
|
15
16
|
"electron",
|
|
16
17
|
"fapi",
|
|
17
18
|
"javascript",
|
|
19
|
+
"netlify",
|
|
20
|
+
"next",
|
|
18
21
|
"nextjs",
|
|
19
22
|
"node",
|
|
20
23
|
"nodejs",
|
|
@@ -23,7 +26,8 @@
|
|
|
23
26
|
"oidc",
|
|
24
27
|
"openid-connect",
|
|
25
28
|
"openid",
|
|
26
|
-
"vercel
|
|
29
|
+
"vercel",
|
|
30
|
+
"workers"
|
|
27
31
|
],
|
|
28
32
|
"homepage": "https://github.com/panva/oauth4webapi",
|
|
29
33
|
"repository": "panva/oauth4webapi",
|
|
@@ -57,22 +61,22 @@
|
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
|
59
63
|
"@esbuild-kit/esm-loader": "^2.5.0",
|
|
60
|
-
"@types/node": "^18.11.
|
|
64
|
+
"@types/node": "^18.11.7",
|
|
61
65
|
"@types/qunit": "^2.19.3",
|
|
62
|
-
"ava": "^
|
|
63
|
-
"edge-runtime": "^
|
|
64
|
-
"jose": "^4.10.
|
|
65
|
-
"patch-package": "^6.
|
|
66
|
+
"ava": "^5.0.1",
|
|
67
|
+
"edge-runtime": "^2.0.0",
|
|
68
|
+
"jose": "^4.10.4",
|
|
69
|
+
"patch-package": "^6.5.0",
|
|
66
70
|
"prettier": "^2.7.1",
|
|
67
71
|
"prettier-plugin-jsdoc": "^0.4.2",
|
|
68
|
-
"qunit": "^2.19.
|
|
72
|
+
"qunit": "^2.19.3",
|
|
69
73
|
"testcafe": "^2.0.1",
|
|
70
74
|
"testcafe-browser-provider-browserstack": "^1.14.0",
|
|
71
75
|
"timekeeper": "^2.2.0",
|
|
72
|
-
"typedoc": "^0.23.
|
|
76
|
+
"typedoc": "^0.23.19",
|
|
73
77
|
"typedoc-plugin-markdown": "^3.13.6",
|
|
74
78
|
"typescript": "^4.8.4",
|
|
75
|
-
"undici": "^5.
|
|
79
|
+
"undici": "^5.12.0",
|
|
76
80
|
"workerd": "^1.20220926.3"
|
|
77
81
|
}
|
|
78
82
|
}
|