The User Object
export type User = {
id: string;
username: string;
displayName: string;
email: string | undefined;
avatarImgUrl: string;
hasRole: (role: string) => boolean;
};Last updated
Was this helpful?
Was this helpful?
import type { CreateUser } from "oidc-spa/core";
import { z } from "zod";
import avatarFallbackSvgUrl from "./assets/avatarFallback.svg";
// App-level user shape exposed by `useOidc()`.
// You decide what an user should looks like!
export type User = {
id: string;
username: string;
displayName: string;
email: string | undefined;
avatarImgUrl: string;
isRealmAdmin: boolean;
userInfo: {
sub: string;
[claim: string]: unknown;
};
keycloakUserProfile?: import("oidc-spa/keycloak").KeycloakProfile;
};
// The function that oidc-spa will call to create the user object,
// gathering information from different sources depending of what you need.
export const createUser: CreateUser<User> = async ({
decodedIdToken: decodedIdToken_generic,
accessToken,
fetchUserInfo,
issuerUri
}) => {
/* ================= Possible source: ID token claims. ====================== */
const DecodedIdToken = z.object({
sub: z.string(),
name: z.string(),
picture: z.string().optional(),
email: z.string().email().optional(),
preferred_username: z.string().optional()
});
const decodedIdToken = DecodedIdToken.parse(decodedIdToken_generic);
/* ================== Possible source: access token claims. ================== */
// This is pragmatic, but not textbook OIDC: clients should usually
// treat access tokens as opaque, and some providers do not issue JWTs.
const DecodedAccessToken = z.object({
realm_access: z.object({ roles: z.array(z.string()) }).optional()
});
const { decodeJwt } = await import("oidc-spa/decode-jwt");
const { isKeycloak } = await import("oidc-spa/keycloak");
const decodedAccessToken = !isKeycloak({ issuerUri })
? undefined
: DecodedAccessToken.parse(decodeJwt(accessToken));
/* ================= Possible source: your own API. ========================= */
// const { fetchWithAuth } = await import("./oidc");
// const userFromApi = await fetchWithAuth("/api/user").then(r => r.json());
/* ================= Possible source: the standard OIDC UserInfo endpoint. == */
const userInfo = await fetchUserInfo();
/* ================= Possible source: provider-specific endpoints. ========== */
const { createKeycloakUtils } = await import("oidc-spa/keycloak");
const keycloakUtils = isKeycloak({ issuerUri }) ?
createKeycloakUtils({ issuerUri }) : undefined;
const keycloakUserProfile = await keycloakUtils?.fetchUserProfile({ accessToken });
/* ================== Merging =============================================== */
// Merge whichever sources you decided to use into the single
// `User` shape consumed by the rest of the app.
const user: User = {
id: decodedIdToken.sub,
username: decodedIdToken.preferred_username ?? decodedIdToken.sub,
displayName: decodedIdToken.name,
avatarImgUrl: decodedIdToken.picture || avatarFallbackSvgUrl,
email: decodedIdToken.email,
isRealmAdmin: decodedAccessToken?.realm_access?.roles.includes("realm-admin") ?? false,
userInfo,
keycloakUserProfile
};
return user;
};
// App-level user returned when the mock implementation is enabled.
export const user_mock: User = {
id: "mock-user",
username: "john.doe",
displayName: "John Doe",
email: undefined,
avatarImgUrl: avatarFallbackSvgUrl,
isRealmAdmin: true,
userInfo: { sub: "1234" },
keycloakUserProfile: undefined
};import { createOidc } from "oidc-spa/core";
import { createUser, user_mock } from "./oidc.user";
import { createMockOidc } from "oidc-spa/core-mock";
const autoLogin = false;
export const prOidc = !import.meta.env.VITE_OIDC_ISSUER
? createMockOidc({
// NOTE: If autoLogin is set to true this option must be removed
isUserInitiallyLoggedIn: false,
// Optional:
mockedParams: {
issuerUri: "https://auth.my-company.com/realms/myrealm",
clientId: "myclient"
},
mockedUser: user_mock,
autoLogin
})
: createOidc({
issuerUri: import.meta.env.VITE_OIDC_ISSUER,
clientId: import.meta.env.VITE_OIDC_CLIENT_ID,
createUser,
autoLogin
});
export async function greetUser() {
const oidc = await prOidc;
if (!oidc.isUserLoggedIn) {
return;
}
const { user } = await oidc.getUser();
alert(`Hello ${user.displayName}`);
}import { oidcSpa } from "oidc-spa/react-spa";
// or "oidc-spa/react-tanstack-start"
import { type User, createUser, user_mock } from "./oidc.user";
export const {
bootstrapOidc,
useOidc,
getOidc,
enforceLogin,
OidcInitializationGate
} = oidcSpa
.withUser<User>({ createUser, user_mock })
.createUtils();
bootstrapOidc(
import.meta.env.VITE_OIDC_USE_MOCK === "true"
? {
implementation: "mock",
isUserInitiallyLoggedIn: true
// You can also override `user_mock` here.
}
: {
implementation: "real",
issuerUri: import.meta.env.VITE_OIDC_ISSUER_URI,
clientId: import.meta.env.VITE_OIDC_CLIENT_ID,
}
);import { useOidc, getOidc } from "~/oidc";
// Accessing the user object in a react component.
function Hero() {
const { user } = useOidc({ assert: "user logged in" });
return <h1>Hello {user.displayName}!</h1>;
}
// ... and outside react:
async function greetUser(){
const oidc = await getOidc({ assert: "user logged in" });
const { user } = await oidc.getUser();
alert(`Hello ${user.displayName}`);
}