[#1] Bare Skeleton with redux store, oidc and router-dom
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Callback } from "./oidcAuth";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import { push } from "redux-first-history";
|
||||
import { setUserData } from "./userSlice";
|
||||
|
||||
export function CallbackResume() {
|
||||
const dispatch = useAppDispatch();
|
||||
const hasRun = useRef(false);
|
||||
const saved = sessionStorage.getItem("redirectState")
|
||||
? JSON.parse(sessionStorage.getItem("redirectState")!)
|
||||
: null;
|
||||
const [tokens, setTokens] = useState<any>();
|
||||
const [userInfo, setUserInfo] = useState<any>();
|
||||
const location = useAppSelector((state) => state.router.location);
|
||||
useEffect(() => {
|
||||
if (hasRun.current) {
|
||||
return;
|
||||
}
|
||||
hasRun.current = true;
|
||||
const runCallback = async () => {
|
||||
try {
|
||||
const data = await Callback(saved?.code_verifier, saved?.state);
|
||||
console.log("data:", data);
|
||||
setTokens(data?.tokenSet);
|
||||
setUserInfo(data?.userinfo);
|
||||
dispatch(setUserData(data?.userinfo));
|
||||
sessionStorage.removeItem("redirectState");
|
||||
// Redirect to main page after successful callback
|
||||
dispatch(push("/"));
|
||||
} catch (e) {
|
||||
console.error("OIDC callback error:", e);
|
||||
}
|
||||
};
|
||||
|
||||
if (saved?.code_verifier) {
|
||||
runCallback();
|
||||
} else {
|
||||
console.warn("Missing redirectState");
|
||||
sessionStorage.removeItem("redirectState");
|
||||
dispatch(push("/"));
|
||||
}
|
||||
}, [dispatch, saved]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Processing OIDC callback...</p>
|
||||
{/* Optionally show loading or debug info */}
|
||||
{tokens && (
|
||||
<ul>
|
||||
<li>
|
||||
ID_Token: <pre>{JSON.stringify(tokens?.id_token)}</pre>
|
||||
</li>
|
||||
<li>
|
||||
Access_Token: <pre>{JSON.stringify(tokens?.access_token)}</pre>
|
||||
</li>
|
||||
<li>
|
||||
User info: <pre>{JSON.stringify(userInfo, null, 2)}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import * as client from "openid-client";
|
||||
|
||||
export const clientConfig = {
|
||||
url: process.env.REACT_APP_SSO_BASE_URL || "",
|
||||
client_id: process.env.REACT_APP_SSO_CLIENT_ID || "",
|
||||
client_secret: process.env.REACT_APP_SSO_CLIENT_SECRET || "",
|
||||
scope: process.env.REACT_APP_SSO_SCOPE || "",
|
||||
redirect_uri: process.env.REACT_APP_SSO_REDIRECT_URI || "",
|
||||
response_type: process.env.REACT_APP_SSO_RESPONSE_TYPE || "",
|
||||
code_challenge_method: process.env.REACT_APP_SSO_CODE_CHALLENGE_METHOD || "",
|
||||
post_logout_redirect_uri:
|
||||
process.env.REACT_APP_SSO_POST_LOGOUT_REDIRECT || "",
|
||||
};
|
||||
|
||||
export async function getClientConfig() {
|
||||
console.log(process.env.REACT_APP_SSO_BASE_URL)
|
||||
return await client.discovery(
|
||||
new URL(clientConfig.url),
|
||||
clientConfig.client_id,
|
||||
clientConfig.client_secret
|
||||
);
|
||||
}
|
||||
|
||||
export async function Auth() {
|
||||
console.log("bap");
|
||||
let code_verifier = client.randomPKCECodeVerifier();
|
||||
let code_challenge = await client.calculatePKCECodeChallenge(code_verifier);
|
||||
const openIdClientConfig = await getClientConfig();
|
||||
let parameters: Record<string, string> = {
|
||||
redirect_uri: clientConfig.redirect_uri,
|
||||
scope: clientConfig.scope!,
|
||||
code_challenge,
|
||||
code_challenge_method: clientConfig.code_challenge_method,
|
||||
};
|
||||
let state!: string;
|
||||
if (!openIdClientConfig.serverMetadata().supportsPKCE()) {
|
||||
state = client.randomState();
|
||||
parameters.state = state;
|
||||
}
|
||||
let redirectTo = client.buildAuthorizationUrl(openIdClientConfig, parameters);
|
||||
console.log(redirectTo);
|
||||
|
||||
return { redirectTo, code_verifier, state };
|
||||
}
|
||||
|
||||
export async function Logout() {
|
||||
const openIdClientConfig = await getClientConfig();
|
||||
const endSessionUrl = client.buildEndSessionUrl(openIdClientConfig, {
|
||||
post_logout_redirect_uri: clientConfig.post_logout_redirect_uri,
|
||||
});
|
||||
|
||||
return endSessionUrl;
|
||||
}
|
||||
|
||||
export async function Callback(code_verifier: string, state: any) {
|
||||
try {
|
||||
const openIdClientConfig = await getClientConfig();
|
||||
const currentUrl = new URL(window.location.href);
|
||||
|
||||
console.log("Callback URL:", currentUrl.toString());
|
||||
console.log("Code verifier:", code_verifier);
|
||||
console.log("State:", state);
|
||||
|
||||
const tokenSet = await client.authorizationCodeGrant(
|
||||
openIdClientConfig,
|
||||
currentUrl,
|
||||
{
|
||||
pkceCodeVerifier: code_verifier,
|
||||
expectedState: state,
|
||||
}
|
||||
);
|
||||
|
||||
const { access_token } = tokenSet;
|
||||
const claims = tokenSet.claims()!;
|
||||
const { sub } = claims;
|
||||
|
||||
const userinfo = await client.fetchUserInfo(
|
||||
openIdClientConfig,
|
||||
access_token,
|
||||
sub
|
||||
);
|
||||
|
||||
return { tokenSet, userinfo };
|
||||
} catch (e) {
|
||||
console.error("Token grant error:", e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface userData {
|
||||
email: string;
|
||||
sid: string;
|
||||
sub: string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { userData } from "./userDataTypes";
|
||||
|
||||
export const userSlice = createSlice({
|
||||
name: "user",
|
||||
initialState: {
|
||||
userData: null as unknown as userData,
|
||||
},
|
||||
reducers: {
|
||||
setUserData: (state, action) => {
|
||||
state.userData = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { setUserData } = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
Reference in New Issue
Block a user