@axa-fr/react-oidc 6.3.0 → 6.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/dist/OidcServiceWorker.js +61 -8
- package/dist/ReactOidc.d.ts +1 -1
- package/dist/ReactOidc.d.ts.map +1 -1
- package/dist/ReactOidc.js.map +1 -1
- package/dist/vanilla/initSession.d.ts +4 -0
- package/dist/vanilla/initSession.d.ts.map +1 -1
- package/dist/vanilla/initSession.js +23 -1
- package/dist/vanilla/initSession.js.map +1 -1
- package/dist/vanilla/initWorker.d.ts +4 -0
- package/dist/vanilla/initWorker.d.ts.map +1 -1
- package/dist/vanilla/initWorker.js +14 -1
- package/dist/vanilla/initWorker.js.map +1 -1
- package/dist/vanilla/oidc.d.ts +3 -0
- package/dist/vanilla/oidc.d.ts.map +1 -1
- package/dist/vanilla/oidc.js +64 -22
- package/dist/vanilla/oidc.js.map +1 -1
- package/dist/vanilla/parseTokens.d.ts +1 -0
- package/dist/vanilla/parseTokens.d.ts.map +1 -1
- package/dist/vanilla/parseTokens.js +31 -2
- package/dist/vanilla/parseTokens.js.map +1 -1
- package/package.json +1 -1
- package/src/oidc/ReactOidc.tsx +1 -1
- package/src/oidc/vanilla/OidcServiceWorker.js +61 -8
- package/src/oidc/vanilla/initSession.ts +23 -2
- package/src/oidc/vanilla/initWorker.ts +12 -0
- package/src/oidc/vanilla/oidc.ts +43 -6
- package/src/oidc/vanilla/parseTokens.ts +33 -1
package/src/oidc/vanilla/oidc.ts
CHANGED
|
@@ -19,7 +19,7 @@ import timer from './timer';
|
|
|
19
19
|
import {CheckSessionIFrame} from "./checkSessionIFrame"
|
|
20
20
|
import {getParseQueryStringFromLocation} from "./route-utils";
|
|
21
21
|
import {AuthorizationServiceConfigurationJson} from "@openid/appauth/src/authorization_service_configuration";
|
|
22
|
-
import {computeTimeLeft, isTokensValid, parseOriginalTokens, setTokens} from "./parseTokens";
|
|
22
|
+
import {computeTimeLeft, isTokensOidcValid, isTokensValid, parseOriginalTokens, setTokens} from "./parseTokens";
|
|
23
23
|
|
|
24
24
|
const performTokenRequestAsync= async (url, details, extras) => {
|
|
25
25
|
|
|
@@ -80,10 +80,12 @@ const internalFetch = async (url, headers, numberRetry=0) => {
|
|
|
80
80
|
|
|
81
81
|
export interface OidcAuthorizationServiceConfigurationJson extends AuthorizationServiceConfigurationJson{
|
|
82
82
|
check_session_iframe?: string;
|
|
83
|
+
issuer:string;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceConfiguration{
|
|
86
87
|
private check_session_iframe: string;
|
|
88
|
+
private issuer: string;
|
|
87
89
|
|
|
88
90
|
constructor(request: any) {
|
|
89
91
|
super(request);
|
|
@@ -92,6 +94,7 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
|
|
|
92
94
|
this.revocationEndpoint = request.revocation_endpoint;
|
|
93
95
|
this.userInfoEndpoint = request.userinfo_endpoint;
|
|
94
96
|
this.check_session_iframe = request.check_session_iframe;
|
|
97
|
+
this.issuer = request.issuer;
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
}
|
|
@@ -113,6 +116,7 @@ export interface AuthorityConfiguration {
|
|
|
113
116
|
end_session_endpoint?: string;
|
|
114
117
|
userinfo_endpoint?: string;
|
|
115
118
|
check_session_iframe?:string;
|
|
119
|
+
issuer:string;
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
export type OidcConfiguration = {
|
|
@@ -519,6 +523,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
519
523
|
token_endpoint: authorityConfiguration.token_endpoint,
|
|
520
524
|
userinfo_endpoint: authorityConfiguration.userinfo_endpoint,
|
|
521
525
|
check_session_iframe: authorityConfiguration.check_session_iframe,
|
|
526
|
+
issuer: authorityConfiguration.issuer,
|
|
522
527
|
});
|
|
523
528
|
}
|
|
524
529
|
|
|
@@ -636,23 +641,38 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
636
641
|
scope = configuration.scope;
|
|
637
642
|
}
|
|
638
643
|
|
|
644
|
+
const randomString = function(length) {
|
|
645
|
+
let text = "";
|
|
646
|
+
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
647
|
+
for(let i = 0; i < length; i++) {
|
|
648
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
649
|
+
}
|
|
650
|
+
return text;
|
|
651
|
+
}
|
|
652
|
+
|
|
639
653
|
setLoginParams(this.configurationName, redirectUri, {callbackPath: url, extras, state});
|
|
640
|
-
|
|
654
|
+
const extraFinal = extras ?? configuration.extras ?? {};
|
|
655
|
+
if(!extraFinal.nonce) {
|
|
656
|
+
extraFinal["nonce"] = randomString(12);
|
|
657
|
+
}
|
|
658
|
+
const nonce = {"nonce":extraFinal.nonce};
|
|
641
659
|
let serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
|
|
642
660
|
const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
|
|
643
661
|
let storage;
|
|
644
662
|
if (serviceWorker) {
|
|
645
663
|
serviceWorker.startKeepAliveServiceWorker();
|
|
646
664
|
await serviceWorker.initAsync(oidcServerConfiguration, "loginAsync");
|
|
665
|
+
await serviceWorker.setNonceAsync(nonce);
|
|
647
666
|
storage = new MemoryStorageBackend(serviceWorker.saveItemsAsync, {});
|
|
648
667
|
await storage.setItem("dummy", {});
|
|
668
|
+
|
|
649
669
|
} else {
|
|
650
670
|
const session = initSession(this.configurationName, redirectUri);
|
|
671
|
+
await session.setNonceAsync(nonce);
|
|
651
672
|
storage = new MemoryStorageBackend(session.saveItemsAsync, {});
|
|
652
673
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
674
|
+
|
|
675
|
+
|
|
656
676
|
// @ts-ignore
|
|
657
677
|
const queryStringUtil = redirectUri.includes("#") ? new HashQueryStringUtils() : new NoHashQueryStringUtils();
|
|
658
678
|
const authorizationHandler = new RedirectRequestHandler(storage, queryStringUtil, window.location, new DefaultCrypto());
|
|
@@ -774,6 +794,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
774
794
|
const sessionState = queryParams.session_state;
|
|
775
795
|
const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
|
|
776
796
|
let storage = null;
|
|
797
|
+
let nonceData = null;
|
|
777
798
|
if(serviceWorker){
|
|
778
799
|
serviceWorker.startKeepAliveServiceWorker();
|
|
779
800
|
this.serviceWorker = serviceWorker;
|
|
@@ -786,14 +807,16 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
786
807
|
}
|
|
787
808
|
await storage.removeItem("dummy");
|
|
788
809
|
await serviceWorker.setSessionStateAsync(sessionState);
|
|
810
|
+
nonceData = await serviceWorker.getNonceAsync();
|
|
789
811
|
}else{
|
|
790
|
-
|
|
791
812
|
this.session = initSession(this.configurationName, redirectUri, configuration.storage ?? sessionStorage);
|
|
792
813
|
const session = initSession(this.configurationName, redirectUri);
|
|
793
814
|
session.setSessionState(sessionState);
|
|
794
815
|
const items = await session.loadItemsAsync();
|
|
795
816
|
storage = new MemoryStorageBackend(session.saveItemsAsync, items);
|
|
817
|
+
nonceData = await session.getNonceAsync();
|
|
796
818
|
}
|
|
819
|
+
|
|
797
820
|
return new Promise((resolve, reject) => {
|
|
798
821
|
// @ts-ignore
|
|
799
822
|
let queryStringUtil = new NoHashQueryStringUtils();
|
|
@@ -853,6 +876,16 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
853
876
|
if (serviceWorker) {
|
|
854
877
|
const {tokens} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
|
|
855
878
|
tokenResponse = tokens;
|
|
879
|
+
};
|
|
880
|
+
if(!isTokensOidcValid(tokenResponse, nonceData.nonce, oidcServerConfiguration)){
|
|
881
|
+
const exception = new Error("Tokens are not OpenID valid");
|
|
882
|
+
if(timeoutId) {
|
|
883
|
+
clearTimeout(timeoutId);
|
|
884
|
+
this.timeoutId=null;
|
|
885
|
+
this.publishEvent(eventNames.loginCallbackAsync_error, exception);
|
|
886
|
+
console.error(exception);
|
|
887
|
+
reject(exception);
|
|
888
|
+
}
|
|
856
889
|
}
|
|
857
890
|
|
|
858
891
|
// @ts-ignore
|
|
@@ -971,6 +1004,10 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
971
1004
|
const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
|
|
972
1005
|
const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
|
|
973
1006
|
if (tokenResponse.success) {
|
|
1007
|
+
if(!isTokensOidcValid(tokenResponse.data, null, oidcServerConfiguration)){
|
|
1008
|
+
this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token return not valid tokens` });
|
|
1009
|
+
return {tokens:null, status:"SESSION_LOST"};
|
|
1010
|
+
}
|
|
974
1011
|
this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
|
|
975
1012
|
this.publishEvent(Oidc.eventNames.token_renewed, {});
|
|
976
1013
|
return {tokens: tokenResponse.data, status:"LOGGED_IN"};
|
|
@@ -51,7 +51,7 @@ export const setTokens = (tokens) =>{
|
|
|
51
51
|
else {
|
|
52
52
|
accessTokenPayload = extractAccessTokenPayload(tokens);
|
|
53
53
|
}
|
|
54
|
-
const _idTokenPayload = idTokenPayload(tokens.idToken);
|
|
54
|
+
const _idTokenPayload = tokens.idTokenPayload ? tokens.idTokenPayload : idTokenPayload(tokens.idToken);
|
|
55
55
|
|
|
56
56
|
const idTokenExipreAt =(_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp: Number.MAX_VALUE;
|
|
57
57
|
const accessTokenExpiresAt = (accessTokenPayload && accessTokenPayload.exp)? accessTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
|
|
@@ -61,6 +61,7 @@ export const setTokens = (tokens) =>{
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
|
|
64
65
|
export const parseOriginalTokens= (tokens) =>{
|
|
65
66
|
if(!tokens){
|
|
66
67
|
return null;
|
|
@@ -104,4 +105,35 @@ export const isTokensValid= (tokens) =>{
|
|
|
104
105
|
return false;
|
|
105
106
|
}
|
|
106
107
|
return computeTimeLeft(0, tokens.expiresAt) > 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation (excluding rules #1, #4, #5, #7, #8, #12, and #13 which did not apply).
|
|
111
|
+
// https://github.com/openid/AppAuth-JS/issues/65
|
|
112
|
+
export const isTokensOidcValid =(tokens, nonce, oidcServerConfiguration) =>{
|
|
113
|
+
if(tokens.idTokenPayload) {
|
|
114
|
+
const idTokenPayload = tokens.idTokenPayload;
|
|
115
|
+
// 2: The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) MUST exactly match the value of the iss (issuer) Claim.
|
|
116
|
+
if(oidcServerConfiguration.issuer !== idTokenPayload.iss){
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
// 3: The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.
|
|
120
|
+
|
|
121
|
+
// 6: If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.
|
|
122
|
+
|
|
123
|
+
// 9: The current time MUST be before the time represented by the exp Claim.
|
|
124
|
+
const currentTimeUnixSecond = new Date().getTime() /1000;
|
|
125
|
+
if(idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
// 10: The iat Claim can be used to reject tokens that were issued too far away from the current time, limiting the amount of time that nonces need to be stored to prevent attacks. The acceptable range is Client specific.
|
|
129
|
+
const timeInSevenDays = 60 * 60 * 24 * 7;
|
|
130
|
+
if(idTokenPayload.iat && (idTokenPayload.iat + timeInSevenDays) < currentTimeUnixSecond) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
// 11: If a nonce value was sent in the Authentication Request, a nonce Claim MUST be present and its value checked to verify that it is the same value as the one that was sent in the Authentication Request. The Client SHOULD check the nonce value for replay attacks. The precise method for detecting replay attacks is Client specific.
|
|
134
|
+
if (idTokenPayload.nonce && idTokenPayload.nonce !== nonce) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
107
139
|
}
|