import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import { AnyAction } from "redux"; import { ThunkAction } from "redux-thunk"; import { RootState } from "./store"; type OpenIdConfigPayload = { issuer: string; authorization_endpoint: string; end_session_endpoint?: string; token_endpoint: string; grant_types_supported: string[]; }; // Async actions using createAsyncThunk export const fetchOpenIdConfiguration = createAsyncThunk( "openIdConfig/fetchOpenIdConfiguration", async (openIdConfigUrl: string, { rejectWithValue }) => { const response = await fetch(openIdConfigUrl); if (response.ok) { return await response.json(); } else { return rejectWithValue("Could not fetch identity provider configuration"); } }, { condition: (_, { getState }) => { const state = getState() as RootState; const { isFetching, data, expiry } = state.openIdConfiguration; return !isFetching && (!data || !expiry || Date.now() > expiry * 1000); }, }, ); export const fetchOpenIdConfigurationIfNecessary = (openIdConfigUrl: string): ThunkAction => async (dispatch, getState) => { const { isFetching, expiry } = getState().openIdConfiguration; if (isFetching || (expiry && Date.now() < expiry * 1000)) return; return dispatch(fetchOpenIdConfiguration(openIdConfigUrl)); }; export type OIDCSliceState = { isFetching: boolean; hasAttempted: boolean; data?: OpenIdConfigPayload; expiry?: number; }; const initialState: OIDCSliceState = { isFetching: false, hasAttempted: false, data: undefined, expiry: undefined, }; export const openIdConfigSlice = createSlice({ name: "openIdConfiguration", initialState, reducers: {}, extraReducers: (builder) => { builder.addCase(fetchOpenIdConfiguration.pending, (state) => { state.isFetching = true; }); builder.addCase(fetchOpenIdConfiguration.fulfilled, (state, { payload }) => { state.isFetching = false; state.hasAttempted = true; state.data = payload; state.expiry = Date.now() / 1000 + 3 * 60 * 60; // Cache for 3 hours }); builder.addCase(fetchOpenIdConfiguration.rejected, (state, { error }) => { console.error(error); state.isFetching = false; state.hasAttempted = true; state.data = undefined; state.expiry = undefined; }); }, }); export default openIdConfigSlice.reducer;