oauth4webapi 1.2.2 → 1.4.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 +26 -67
- package/package.json +22 -14
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
|
+
- Bun
|
|
62
|
+
- Cloudflare Workers
|
|
63
|
+
- Deno
|
|
64
|
+
- Electron
|
|
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.4.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,45 +769,9 @@ 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);
|
|
774
|
+
headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
|
|
812
775
|
return fetch(url.href, {
|
|
813
776
|
body,
|
|
814
777
|
headers,
|
|
@@ -847,7 +810,7 @@ export function getValidatedIdTokenClaims(ref) {
|
|
|
847
810
|
}
|
|
848
811
|
return idTokenClaims.get(ref);
|
|
849
812
|
}
|
|
850
|
-
async function processGenericAccessTokenResponse(as, client, response, options, ignoreIdToken = false, ignoreRefreshToken = false) {
|
|
813
|
+
async function processGenericAccessTokenResponse(as, client, response, options, ignoreIdToken = false, ignoreRefreshToken = false, skipSignatureCheck = false) {
|
|
851
814
|
assertAs(as);
|
|
852
815
|
assertClient(client);
|
|
853
816
|
if (!(response instanceof Response)) {
|
|
@@ -897,10 +860,9 @@ async function processGenericAccessTokenResponse(as, client, response, options,
|
|
|
897
860
|
throw new OPE('"response" body "id_token" property must be a non-empty string');
|
|
898
861
|
}
|
|
899
862
|
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))
|
|
863
|
+
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
|
|
864
|
+
? getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options)
|
|
865
|
+
: noSignatureCheck)
|
|
904
866
|
.then(validatePresence.bind(undefined, ['aud', 'exp', 'iat', 'iss', 'sub']))
|
|
905
867
|
.then(validateIssuer.bind(undefined, as.issuer))
|
|
906
868
|
.then(validateAudience.bind(undefined, client.client_id));
|
|
@@ -910,19 +872,13 @@ async function processGenericAccessTokenResponse(as, client, response, options,
|
|
|
910
872
|
if (client.require_auth_time && typeof claims.auth_time !== 'number') {
|
|
911
873
|
throw new OPE('unexpected ID Token "auth_time" (authentication time) claim value');
|
|
912
874
|
}
|
|
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
875
|
idTokenClaims.set(json, claims);
|
|
920
876
|
}
|
|
921
877
|
}
|
|
922
878
|
return json;
|
|
923
879
|
}
|
|
924
880
|
export async function processRefreshTokenResponse(as, client, response, options) {
|
|
925
|
-
return processGenericAccessTokenResponse(as, client, response, options);
|
|
881
|
+
return processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
926
882
|
}
|
|
927
883
|
function validateOptionalAudience(expected, result) {
|
|
928
884
|
if (result.claims.aud !== undefined) {
|
|
@@ -993,7 +949,7 @@ function validatePresence(required, result) {
|
|
|
993
949
|
export const expectNoNonce = Symbol();
|
|
994
950
|
export const skipAuthTimeCheck = Symbol();
|
|
995
951
|
export async function processAuthorizationCodeOpenIDResponse(as, client, response, expectedNonce, maxAge, options) {
|
|
996
|
-
const result = await processGenericAccessTokenResponse(as, client, response, options);
|
|
952
|
+
const result = await processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
997
953
|
if (isOAuth2Error(result)) {
|
|
998
954
|
return result;
|
|
999
955
|
}
|
|
@@ -1137,10 +1093,9 @@ export async function processIntrospectionResponse(as, client, response, options
|
|
|
1137
1093
|
}
|
|
1138
1094
|
let json;
|
|
1139
1095
|
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))
|
|
1096
|
+
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
|
|
1097
|
+
? getPublicSigKeyFromIssuerJwksUri.bind(undefined, as, options)
|
|
1098
|
+
: noSignatureCheck)
|
|
1144
1099
|
.then(checkJwtType.bind(undefined, 'token-introspection+jwt'))
|
|
1145
1100
|
.then(validatePresence.bind(undefined, ['aud', 'iat', 'iss']))
|
|
1146
1101
|
.then(validateIssuer.bind(undefined, as.issuer))
|
|
@@ -1260,8 +1215,9 @@ function subtleAlgorithm(key) {
|
|
|
1260
1215
|
}
|
|
1261
1216
|
throw new UnsupportedOperationError();
|
|
1262
1217
|
}
|
|
1218
|
+
const noSignatureCheck = Symbol();
|
|
1263
1219
|
async function validateJwt(jws, checkAlg, getKey) {
|
|
1264
|
-
const { 0: protectedHeader, 1: payload, 2:
|
|
1220
|
+
const { 0: protectedHeader, 1: payload, 2: encodedSignature, length } = jws.split('.');
|
|
1265
1221
|
if (length === 5) {
|
|
1266
1222
|
throw new UnsupportedOperationError('JWE structure JWTs are not supported');
|
|
1267
1223
|
}
|
|
@@ -1282,11 +1238,14 @@ async function validateJwt(jws, checkAlg, getKey) {
|
|
|
1282
1238
|
if (header.crit !== undefined) {
|
|
1283
1239
|
throw new OPE('unexpected JWT "crit" header parameter');
|
|
1284
1240
|
}
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1241
|
+
const signature = b64u(encodedSignature);
|
|
1242
|
+
if (getKey !== noSignatureCheck) {
|
|
1243
|
+
const key = await getKey(header);
|
|
1244
|
+
const input = `${protectedHeader}.${payload}`;
|
|
1245
|
+
const verified = await crypto.subtle.verify(subtleAlgorithm(key), key, signature, buf(input));
|
|
1246
|
+
if (!verified) {
|
|
1247
|
+
throw new OPE('JWT signature verification failed');
|
|
1248
|
+
}
|
|
1290
1249
|
}
|
|
1291
1250
|
let claims;
|
|
1292
1251
|
try {
|
|
@@ -1331,7 +1290,7 @@ async function validateJwt(jws, checkAlg, getKey) {
|
|
|
1331
1290
|
throw new OPE('unexpected JWT "aud" (audience) claim type');
|
|
1332
1291
|
}
|
|
1333
1292
|
}
|
|
1334
|
-
return { header, claims };
|
|
1293
|
+
return { header, claims, signature };
|
|
1335
1294
|
}
|
|
1336
1295
|
export async function validateJwtAuthResponse(as, client, parameters, expectedState, options) {
|
|
1337
1296
|
assertAs(as);
|
|
@@ -1536,7 +1495,7 @@ export async function deviceCodeGrantRequest(as, client, deviceCode, options) {
|
|
|
1536
1495
|
return tokenEndpointRequest(as, client, 'urn:ietf:params:oauth:grant-type:device_code', parameters, options);
|
|
1537
1496
|
}
|
|
1538
1497
|
export async function processDeviceCodeResponse(as, client, response, options) {
|
|
1539
|
-
return processGenericAccessTokenResponse(as, client, response, options);
|
|
1498
|
+
return processGenericAccessTokenResponse(as, client, response, options, undefined, undefined, options?.skipJwtSignatureCheck === true);
|
|
1540
1499
|
}
|
|
1541
1500
|
export async function generateKeyPair(alg, options) {
|
|
1542
1501
|
let algorithm;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oauth4webapi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "OAuth 2 / OpenID Connect for Web Platform API JavaScript runtimes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
"authorization",
|
|
9
9
|
"basic",
|
|
10
10
|
"browser",
|
|
11
|
+
"bun",
|
|
11
12
|
"certified",
|
|
12
13
|
"client",
|
|
13
|
-
"cloudflare
|
|
14
|
+
"cloudflare",
|
|
14
15
|
"deno",
|
|
16
|
+
"edge",
|
|
15
17
|
"electron",
|
|
16
18
|
"fapi",
|
|
17
19
|
"javascript",
|
|
20
|
+
"netlify",
|
|
21
|
+
"next",
|
|
18
22
|
"nextjs",
|
|
19
23
|
"node",
|
|
20
24
|
"nodejs",
|
|
@@ -23,7 +27,8 @@
|
|
|
23
27
|
"oidc",
|
|
24
28
|
"openid-connect",
|
|
25
29
|
"openid",
|
|
26
|
-
"vercel
|
|
30
|
+
"vercel",
|
|
31
|
+
"workers"
|
|
27
32
|
],
|
|
28
33
|
"homepage": "https://github.com/panva/oauth4webapi",
|
|
29
34
|
"repository": "panva/oauth4webapi",
|
|
@@ -43,36 +48,39 @@
|
|
|
43
48
|
"scripts": {
|
|
44
49
|
"_format": "find src test tap examples conformance -type f -name '*.ts' -o -name '*.mjs' | xargs prettier",
|
|
45
50
|
"build": "rm -rf build && tsc && tsc --declaration true --emitDeclarationOnly true --removeComments false && tsc -p test && tsc -p examples && tsc -p conformance && tsc -p tap",
|
|
46
|
-
"conformance": "bash -c '
|
|
51
|
+
"conformance": "bash -c 'source .node_flags.sh && ava --config conformance/ava.config.mjs'",
|
|
47
52
|
"docs": "patch-package && typedoc",
|
|
48
53
|
"format": "npm run _format -- --write",
|
|
49
54
|
"format-check": "npm run _format -- --check",
|
|
50
55
|
"prepack": "npm run format && npm run docs && ./examples/.update-diffs.sh && git diff --quiet && npm run test && npm run build",
|
|
56
|
+
"tap:browsers": "./tap/.browsers.sh",
|
|
57
|
+
"tap:bun": "./tap/.bun.sh",
|
|
51
58
|
"tap:deno": "./tap/.deno.sh",
|
|
52
59
|
"tap:edge-runtime": "./tap/.edge-runtime.sh",
|
|
60
|
+
"tap:electron": "./tap/.electron.sh",
|
|
53
61
|
"tap:node": "bash -c './tap/.node.sh'",
|
|
54
62
|
"tap:workers": "./tap/.workers.sh",
|
|
55
|
-
"tap:browsers": "./tap/.browsers.sh",
|
|
56
63
|
"test": "bash -c 'source .node_flags.sh && ava'"
|
|
57
64
|
},
|
|
58
65
|
"devDependencies": {
|
|
59
66
|
"@esbuild-kit/esm-loader": "^2.5.0",
|
|
60
|
-
"@types/node": "^18.11.
|
|
67
|
+
"@types/node": "^18.11.9",
|
|
61
68
|
"@types/qunit": "^2.19.3",
|
|
62
|
-
"ava": "^
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
69
|
+
"ava": "^5.0.1",
|
|
70
|
+
"bowser": "^2.11.0",
|
|
71
|
+
"edge-runtime": "^2.0.1",
|
|
72
|
+
"jose": "^4.10.4",
|
|
73
|
+
"patch-package": "^6.5.0",
|
|
66
74
|
"prettier": "^2.7.1",
|
|
67
75
|
"prettier-plugin-jsdoc": "^0.4.2",
|
|
68
|
-
"qunit": "^2.19.
|
|
69
|
-
"testcafe": "^2.0.
|
|
76
|
+
"qunit": "^2.19.3",
|
|
77
|
+
"testcafe": "^2.0.2",
|
|
70
78
|
"testcafe-browser-provider-browserstack": "^1.14.0",
|
|
71
79
|
"timekeeper": "^2.2.0",
|
|
72
|
-
"typedoc": "^0.23.
|
|
80
|
+
"typedoc": "^0.23.20",
|
|
73
81
|
"typedoc-plugin-markdown": "^3.13.6",
|
|
74
82
|
"typescript": "^4.8.4",
|
|
75
|
-
"undici": "^5.
|
|
83
|
+
"undici": "^5.12.0",
|
|
76
84
|
"workerd": "^1.20220926.3"
|
|
77
85
|
}
|
|
78
86
|
}
|