feat(web): OIDC login button + cookie-based session bootstrap
The SvelteKit /login was username/password only; the legacy comment
even called out 'OIDC SSO ships in M4 when the IdP is wired up'.
Authentik is wired up now, so:
- /api/v1/config exposes ext.oidc when the IdP is configured. Fetch
it on the login page and conditionally render "Sign in with
{provider}", which kicks off /api/v1/oidc/login.
- After PhotoPrism completes the auth code exchange, it sets
`auth_token` + `auth_session` cookies and redirects to siteUrl
(/library/browse by default; the deployment's reverse proxy is
expected to bounce that to /). bootstrapSessionFromCookies()
reads those cookies, calls GET /api/v1/session/<id> with the
cookie's token, and adopts the resulting session into the SPA
store on mount.
- Root layout's auth guard now waits for the bootstrap pass before
punting to /login, so a fresh OIDC return doesn't get redirected
away before the session is read.
This commit is contained in:
@@ -67,6 +67,36 @@ export async function fetchSession(id: string): Promise<PpSessionResponse> {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* After OIDC completes, PhotoPrism redirects to `siteUrl` with two cookies
|
||||
* set: `auth_token` (the X-Auth-Token value) and `auth_session` (the session
|
||||
* UID). If both are present, fetch the matching session and adopt it so the
|
||||
* SPA picks up the OIDC-issued identity without a username/password trip.
|
||||
*
|
||||
* Returns the adopted session, or null when the cookies are missing or stale
|
||||
* (caller treats null as "stay on /login").
|
||||
*/
|
||||
export async function bootstrapSessionFromCookies(): Promise<PpSessionResponse | null> {
|
||||
if (!browser) return null;
|
||||
const read = (name: string): string | null => {
|
||||
const m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]+)'));
|
||||
return m ? decodeURIComponent(m[1]) : null;
|
||||
};
|
||||
const token = read('auth_token');
|
||||
const sid = read('auth_session');
|
||||
if (!token || !sid) return null;
|
||||
// Prime the http client so the X-Auth-Token interceptor fires.
|
||||
session.accessToken = token;
|
||||
try {
|
||||
const resp = await fetchSession(sid);
|
||||
adoptSession(resp);
|
||||
return resp;
|
||||
} catch {
|
||||
session.accessToken = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConfig(): Promise<PpClientConfig> {
|
||||
const { data } = await http.get<PpClientConfig>('/config');
|
||||
return data;
|
||||
|
||||
Reference in New Issue
Block a user