react-oauth2-code-pkce
Advanced tools
Comparing version 1.11.0 to 1.12.1
@@ -51,2 +51,3 @@ "use strict"; | ||
const [loginInProgress, setLoginInProgress] = (0, Hooks_1.default)('ROCP_loginInProgress', false); | ||
const [refreshInProgress, setRefreshInProgress] = (0, Hooks_1.default)('ROCP_refreshInProgress', false); | ||
const [tokenData, setTokenData] = (0, react_1.useState)(); | ||
@@ -57,4 +58,4 @@ const [idTokenData, setIdTokenData] = (0, react_1.useState)(); | ||
// Set default values for internal config object | ||
const { autoLogin = true, decodeToken = true, scope = '', preLogin = () => null, postLogin = () => null, onRefreshTokenExpire = undefined, } = authConfig; | ||
const config = Object.assign(Object.assign({}, authConfig), { autoLogin: autoLogin, decodeToken: decodeToken, scope: scope, preLogin: preLogin, postLogin: postLogin, onRefreshTokenExpire: onRefreshTokenExpire }); | ||
const { autoLogin = true, clearURL = true, decodeToken = true, scope = '', preLogin = () => null, postLogin = () => null, onRefreshTokenExpire = undefined, } = authConfig; | ||
const config = Object.assign(Object.assign({}, authConfig), { autoLogin: autoLogin, clearURL: clearURL, decodeToken: decodeToken, scope: scope, preLogin: preLogin, postLogin: postLogin, onRefreshTokenExpire: onRefreshTokenExpire }); | ||
(0, validateAuthConfig_1.validateAuthConfig)(config); | ||
@@ -76,6 +77,6 @@ function clearStorage() { | ||
} | ||
function login() { | ||
function login(state) { | ||
clearStorage(); | ||
setLoginInProgress(true); | ||
(0, authentication_1.redirectToLogin)(config); | ||
(0, authentication_1.redirectToLogin)(config, state); | ||
} | ||
@@ -91,3 +92,2 @@ function handleTokenResponse(response) { | ||
setIdToken(response.id_token); | ||
setLoginInProgress(false); | ||
try { | ||
@@ -114,6 +114,7 @@ if (config.decodeToken) | ||
function refreshAccessToken(initial = false) { | ||
// We have a token, but it has expired | ||
if (token && (0, timeUtils_1.epochTimeIsPast)(tokenExpire)) { | ||
// Only refresh if no other instance (tab) is currently refreshing, or it's initial page load | ||
if (token && (0, timeUtils_1.epochTimeIsPast)(tokenExpire) && (!refreshInProgress || initial)) { | ||
// We have a refreshToken, and it is not expired | ||
if (refreshToken && !(0, timeUtils_1.epochTimeIsPast)(refreshTokenExpire)) { | ||
setRefreshInProgress(true); | ||
(0, authentication_1.fetchWithRefreshToken)({ config, refreshToken }) | ||
@@ -142,2 +143,5 @@ .then((result) => handleTokenResponse(result)) | ||
} | ||
}) | ||
.finally(() => { | ||
setRefreshInProgress(false); | ||
}); | ||
@@ -153,5 +157,5 @@ } | ||
} | ||
// Register the 'check for soon expiring access token' interval (Every minute) | ||
// Register the 'check for soon expiring access token' interval (Every 10 seconds) | ||
(0, react_1.useEffect)(() => { | ||
interval = setInterval(() => refreshAccessToken(), 60000); // eslint-disable-line | ||
interval = setInterval(() => refreshAccessToken(), 10000); // eslint-disable-line | ||
return () => clearInterval(interval); | ||
@@ -177,2 +181,9 @@ }, [token]); // This token dependency removes the old, and registers a new Interval when a new token is fetched. | ||
didFetchTokens.current = true; | ||
try { | ||
(0, authentication_1.validateState)(urlParams); | ||
} | ||
catch (e) { | ||
console.error(e); | ||
setError(e.message); | ||
} | ||
// Request token from auth server with the auth code | ||
@@ -191,3 +202,7 @@ (0, authentication_1.fetchTokens)(config) | ||
.finally(() => { | ||
window.history.replaceState(null, '', window.location.pathname); // Clear ugly url params | ||
if (config.clearURL) { | ||
// Clear ugly url params | ||
window.history.replaceState(null, '', window.location.pathname); | ||
} | ||
setLoginInProgress(false); | ||
}); | ||
@@ -194,0 +209,0 @@ } |
import { TInternalConfig, TTokenResponse } from './Types'; | ||
export declare function redirectToLogin(config: TInternalConfig): Promise<void>; | ||
export declare function redirectToLogin(config: TInternalConfig, customState?: string): Promise<void>; | ||
export declare const fetchTokens: (config: TInternalConfig) => Promise<TTokenResponse>; | ||
@@ -9,1 +9,2 @@ export declare const fetchWithRefreshToken: (props: { | ||
export declare function redirectToLogout(config: TInternalConfig, token: string, idToken?: string, state?: string, logoutHint?: string): void; | ||
export declare function validateState(urlParams: URLSearchParams): void; |
@@ -12,11 +12,12 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.redirectToLogout = exports.fetchWithRefreshToken = exports.fetchTokens = exports.redirectToLogin = void 0; | ||
exports.validateState = exports.redirectToLogout = exports.fetchWithRefreshToken = exports.fetchTokens = exports.redirectToLogin = void 0; | ||
const pkceUtils_1 = require("./pkceUtils"); | ||
const httpUtils_1 = require("./httpUtils"); | ||
const codeVerifierStorageKey = 'PKCE_code_verifier'; | ||
function redirectToLogin(config) { | ||
const stateStorageKey = 'ROCP_auth_state'; | ||
function redirectToLogin(config, customState) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// Create and store a random string in localStorage, used as the 'code_verifier' | ||
const codeVerifier = (0, pkceUtils_1.generateRandomString)(96); | ||
localStorage.setItem(codeVerifierStorageKey, codeVerifier); | ||
sessionStorage.setItem(codeVerifierStorageKey, codeVerifier); | ||
// Hash and Base64URL encode the code_verifier, used as the 'code_challenge' | ||
@@ -26,2 +27,8 @@ (0, pkceUtils_1.generateCodeChallenge)(codeVerifier).then((codeChallenge) => { | ||
const params = new URLSearchParams(Object.assign({ response_type: 'code', client_id: config.clientId, scope: config.scope, redirect_uri: config.redirectUri, code_challenge: codeChallenge, code_challenge_method: 'S256' }, config.extraAuthParameters)); | ||
sessionStorage.removeItem(stateStorageKey); | ||
const state = customState !== null && customState !== void 0 ? customState : config.state; | ||
if (state) { | ||
sessionStorage.setItem(stateStorageKey, state); | ||
params.append('state', state); | ||
} | ||
// Call any preLogin function in authConfig | ||
@@ -59,3 +66,3 @@ if (config === null || config === void 0 ? void 0 : config.preLogin) | ||
const authCode = urlParams.get('code'); | ||
const codeVerifier = window.localStorage.getItem(codeVerifierStorageKey); | ||
const codeVerifier = window.sessionStorage.getItem(codeVerifierStorageKey); | ||
if (!authCode) { | ||
@@ -97,1 +104,9 @@ throw Error("Parameter 'code' not found in URL. \nHas authentication taken place?"); | ||
exports.redirectToLogout = redirectToLogout; | ||
function validateState(urlParams) { | ||
const receivedState = urlParams.get('state'); | ||
const loadedState = sessionStorage.getItem(stateStorageKey); | ||
if (receivedState !== loadedState) { | ||
throw new Error('"state" value received from authentication server does no match client request. Possible cross-site request forgery'); | ||
} | ||
} | ||
exports.validateState = validateState; |
@@ -16,2 +16,8 @@ "use strict"; | ||
const setValue = (value) => { | ||
if (!value) { | ||
// Delete item if set to undefined. This avoids warning on loading invalid json | ||
setStoredValue(value); | ||
localStorage.removeItem(key); | ||
return; | ||
} | ||
try { | ||
@@ -18,0 +24,0 @@ const valueToStore = value instanceof Function ? value(storedValue) : value; |
@@ -6,5 +6,5 @@ import { TTokenResponse } from './Types'; | ||
* Check if the Access Token has expired. | ||
* Will return True if the token has expired, OR there is less than 5min until it expires. | ||
* Will return True if the token has expired, OR there is less than 30 seconds until it expires. | ||
*/ | ||
export declare function epochTimeIsPast(timestamp: number): boolean; | ||
export declare function getRefreshExpiresIn(tokenExpiresIn: number, response: TTokenResponse): number; |
@@ -10,7 +10,7 @@ "use strict"; | ||
* Check if the Access Token has expired. | ||
* Will return True if the token has expired, OR there is less than 5min until it expires. | ||
* Will return True if the token has expired, OR there is less than 30 seconds until it expires. | ||
*/ | ||
function epochTimeIsPast(timestamp) { | ||
const now = Math.round(Date.now()) / 1000; | ||
const nowWithBuffer = now + 120; | ||
const nowWithBuffer = now + 30; | ||
return nowWithBuffer >= timestamp; | ||
@@ -17,0 +17,0 @@ } |
@@ -36,3 +36,3 @@ import { ReactNode } from 'react'; | ||
logOut: (state?: string, logoutHint?: string) => void; | ||
login: () => void; | ||
login: (state?: string) => void; | ||
error: string | null; | ||
@@ -50,2 +50,3 @@ tokenData?: TTokenData; | ||
scope?: string; | ||
state?: string; | ||
logoutEndpoint?: string; | ||
@@ -58,2 +59,3 @@ logoutRedirect?: string; | ||
autoLogin?: boolean; | ||
clearURL?: boolean; | ||
extraAuthParams?: { | ||
@@ -83,2 +85,3 @@ [key: string]: string | boolean | number; | ||
scope: string; | ||
state?: string; | ||
logoutEndpoint?: string; | ||
@@ -91,2 +94,3 @@ logoutRedirect?: string; | ||
autoLogin: boolean; | ||
clearURL: boolean; | ||
extraAuthParams?: { | ||
@@ -93,0 +97,0 @@ [key: string]: string | boolean | number; |
{ | ||
"name": "react-oauth2-code-pkce", | ||
"version": "1.11.0", | ||
"version": "1.12.1", | ||
"description": "Provider agnostic react package for OAuth2 Authorization Code flow with PKCE", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -80,4 +80,5 @@ # react-oauth2-code-pkce · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/soofstad/react-oauth2-pkce/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/react-oauth2-code-pkce)](https://www.npmjs.com/package/react-oauth2-code-pkce) ![CI](https://github.com/soofstad/react-oauth2-pkce/actions/workflows/tests.yaml/badge.svg) | ||
tokenData?: TTokenData | ||
// Function to trigger login. | ||
login: () => void | ||
// Function to trigger login. | ||
// If you want to use 'state', you might want to set 'clearURL' configuration parameter to 'false'. | ||
login: (state?: string) => void | ||
// Function to trigger logout from authentication provider. You may provide optional 'state', and 'logout_hint' values. | ||
@@ -123,2 +124,4 @@ // See https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout for details. | ||
scope?: string // default: '' | ||
// Optional state value. Will often make more sense to provide the state in a call to the 'login()' function | ||
state?: string // default: null | ||
// Which URL to call for logging out of the auth provider | ||
@@ -143,2 +146,4 @@ logoutEndpoint?: string // default: null | ||
autoLogin?: boolean // default: true | ||
// Set to false if you need to access the urlParameters sent back from the login server. | ||
clearURL?: boolean // default: true | ||
// Can be used to provide any non-standard parameters to the authentication request | ||
@@ -145,0 +150,0 @@ extraAuthParameters?: { [key: string]: string | boolean | number } // default: null |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42699
709
186