UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

118 lines (117 loc) 4.22 kB
import { $hook, $inject, $module, Alepha } from "alepha"; import { $logger } from "alepha/logger"; import { ReactBrowserProvider, Redirection } from "alepha/react/router"; import { currentUserAtom } from "alepha/security"; import { HttpClient } from "alepha/server"; import { alephaServerAuthRoutes, tokenResponseSchema, userinfoResponseSchema } from "alepha/server/auth"; import { LinkProvider } from "alepha/server/links"; import { useAlepha, useStore } from "alepha/react"; //#region ../../src/react/auth/services/ReactAuth.ts /** * Browser, SSR friendly, service to handle authentication. */ var ReactAuth = class { log = $logger(); alepha = $inject(Alepha); httpClient = $inject(HttpClient); linkProvider = $inject(LinkProvider); onBeginTransition = $hook({ on: "react:transition:begin", handler: async (event) => { if (this.alepha.isBrowser()) Object.defineProperty(event.state, "user", { get: () => this.user }); } }); onFetchRequest = $hook({ on: "client:onRequest", handler: async ({ request }) => { if (this.alepha.isBrowser() && this.user) request.credentials ??= "include"; } }); /** * Get the current authenticated user. * * Alias for `alepha.state.get("user")` */ get user() { return this.alepha.store.get(currentUserAtom); } async ping() { const { data } = await this.httpClient.fetch(alephaServerAuthRoutes.userinfo, { schema: { response: userinfoResponseSchema } }); this.alepha.store.set("alepha.server.request.apiLinks", data.api); this.alepha.store.set(currentUserAtom, data.user); return data.user; } can(action) { if (!this.user) return false; return this.linkProvider.can(action); } async login(provider, options) { const realmParam = options.realm ? `&realm=${encodeURIComponent(options.realm)}` : ""; if (options.username || options.password) { const { data } = await this.httpClient.fetch(`${options.hostname || ""}${alephaServerAuthRoutes.token}?provider=${provider}${realmParam}`, { method: "POST", body: JSON.stringify({ username: options.username, password: options.password }), schema: { response: tokenResponseSchema } }); this.alepha.store.set("alepha.server.request.apiLinks", data.api); this.alepha.store.set(currentUserAtom, data.user); return data; } if (this.alepha.isBrowser()) { const browser = this.alepha.inject(ReactBrowserProvider); const redirect = options.redirect || (browser.transitioning ? window.location.origin + browser.transitioning.to : window.location.href); const href = `${window.location.origin}${alephaServerAuthRoutes.login}?provider=${provider}${realmParam}&redirect_uri=${encodeURIComponent(redirect)}`; if (browser.transitioning) throw new Redirection(href); else { window.location.href = href; return {}; } } throw new Redirection(`${alephaServerAuthRoutes.login}?provider=${provider}${realmParam}&redirect_uri=${options.redirect || "/"}`); } logout() { const form = document.createElement("form"); form.method = "POST"; form.action = `${alephaServerAuthRoutes.logout}?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}`; form.style.display = "none"; document.body.appendChild(form); form.submit(); } }; //#endregion //#region ../../src/react/auth/hooks/useAuth.ts const useAuth = () => { const alepha = useAlepha(); const [user] = useStore(currentUserAtom); return { user, logout: () => { alepha.inject(ReactAuth).logout(); }, login: async (provider, options = {}) => { await alepha.inject(ReactAuth).login(provider, options); }, /** * UI permission check — does the current user hold this permission? * Supports exact and wildcard names (e.g. `"admin:*"`, `"admin:user:read"`). * * UI affordance only (show/hide/disable). Real access control is enforced * server-side via `$secure` on the route/action. */ has: (permission) => { return alepha.inject(LinkProvider).can(permission); } }; }; //#endregion //#region ../../src/react/auth/index.browser.ts const AlephaReactAuth = $module({ name: "alepha.react.auth", services: [ReactAuth] }); //#endregion export { AlephaReactAuth, ReactAuth, useAuth }; //# sourceMappingURL=index.browser.js.map