Files
Galaxies-ai-recorder/utils/AuthProvider.tsx
T
Dennis Hundertmark 6ed3300183 Initial commit
Generated by create-expo-app 3.3.0.
2025-04-04 08:33:13 +02:00

177 lines
5.0 KiB
TypeScript

import React, { createContext, useContext, useEffect, useState } from "react";
import * as AuthSession from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import { Platform } from "react-native";
import { Storage } from "./storage";
import { keycloakConfig } from "./keycloakConfig";
import { ActivityIndicator } from "react-native";
// Only call maybeCompleteAuthSession on web platform
if (Platform.OS === "web") {
WebBrowser.maybeCompleteAuthSession();
}
const TOKEN_KEY = "kc_access_token";
const REFRESH_KEY = "kc_refresh_token";
const ID_TOKEN_KEY = "kc_id_token";
const AuthContext = createContext<any>(null);
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const [discovery, setDiscovery] =
useState<AuthSession.DiscoveryDocument | null>(null);
const [accessToken, setAccessToken] = useState<string | null>(null);
const [idToken, setIdToken] = useState<string | null>(null);
const [refreshToken, setRefreshToken] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [isLoaded, setIsLoaded] = useState(false);
const redirectUri = AuthSession.makeRedirectUri({
scheme: keycloakConfig.scheme,
path: Platform.OS === "web" ? "" : "(auth)/index",
});
useEffect(() => {
const loadDiscovery = async () => {
const doc = await AuthSession.fetchDiscoveryAsync(
`${keycloakConfig.url}/realms/${keycloakConfig.realm}`
);
setDiscovery(doc);
};
loadDiscovery();
}, []);
useEffect(() => {
const restore = async () => {
const storedAccess = await Storage.getItem(TOKEN_KEY);
const storedId = await Storage.getItem(ID_TOKEN_KEY);
const storedRefresh = await Storage.getItem(REFRESH_KEY);
if (storedAccess && storedRefresh) {
setAccessToken(storedAccess);
setIdToken(storedId);
setRefreshToken(storedRefresh);
}
setLoading(false);
setIsLoaded(true);
};
restore();
}, []);
const login = async () => {
if (!discovery) return;
const authRequest = new AuthSession.AuthRequest({
responseType: AuthSession.ResponseType.Code,
clientId: keycloakConfig.clientId,
redirectUri,
scopes: ["openid", "profile", "email", "offline_access"],
usePKCE: true,
prompt: AuthSession.Prompt.Login,
});
await authRequest.makeAuthUrlAsync(discovery);
const result = await authRequest.promptAsync(discovery);
if (result.type === "success") {
const tokenResponse = await AuthSession.exchangeCodeAsync(
{
clientId: keycloakConfig.clientId,
code: result.params.code,
redirectUri,
extraParams: {
code_verifier: authRequest.codeVerifier || "",
},
},
discovery
);
const { accessToken, idToken, refreshToken } = tokenResponse;
setAccessToken(accessToken);
setIdToken(idToken ?? null);
setRefreshToken(refreshToken ?? null);
await Storage.setItem(TOKEN_KEY, accessToken || "");
if (idToken) await Storage.setItem(ID_TOKEN_KEY, idToken);
if (refreshToken) await Storage.setItem(REFRESH_KEY, refreshToken);
}
};
const refresh = async () => {
if (!discovery || !refreshToken) return;
const newToken = await AuthSession.refreshAsync(
{
clientId: keycloakConfig.clientId,
refreshToken,
},
discovery
);
setAccessToken(newToken.accessToken || null);
setIdToken(newToken.idToken || null);
setRefreshToken(newToken.refreshToken || null);
await Storage.setItem(TOKEN_KEY, newToken.accessToken || "");
if (newToken.idToken) await Storage.setItem(ID_TOKEN_KEY, newToken.idToken);
if (newToken.refreshToken)
await Storage.setItem(REFRESH_KEY, newToken.refreshToken);
};
const logout = async () => {
if (!discovery || !accessToken) return;
const clientId = keycloakConfig.clientId;
const logOutRedirectUri = AuthSession.makeRedirectUri({
scheme: keycloakConfig.scheme,
path: Platform.OS === "web" ? "" : "home",
});
const logoutUrl = `${
discovery.endSessionEndpoint
}?client_id=${clientId}&post_logout_redirect_uri=${encodeURIComponent(
logOutRedirectUri
)}&id_token_hint=${idToken}`;
const result = await WebBrowser.openAuthSessionAsync(
logoutUrl,
logOutRedirectUri
);
if (result.type === "success") {
setAccessToken(null);
setIdToken(null);
setRefreshToken(null);
await Storage.deleteItem(TOKEN_KEY);
await Storage.deleteItem(ID_TOKEN_KEY);
await Storage.deleteItem(REFRESH_KEY);
}
};
if (!discovery || loading) return <ActivityIndicator />;
return (
<AuthContext.Provider
value={{
accessToken,
idToken,
isLoaded,
refreshToken,
login,
logout,
refresh,
isAuthenticated: !!accessToken,
loading,
}}
>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);