@frigear-nu/auth
Authentication for the frigear ecosystem. Signing in: email/password, password reset, magic links, email
verification, passkeys and OAuth providers (including "Log in with frigear.nu"), with opt-in login/register pages.
Identifying callers: one requireAuth for every way a caller can authenticate: the cookie session, a static
user API token (fnu_… by default, authentication.apiTokens.prefix; apiTokens: false turns them off) and an OAuth access token issued to an application.
Access control: one authorize(event, permission, record?) for every caller. Scopes narrow what a token may do,
and the owner's current roles still apply.
// server/api/tickets.get.ts
const { user } = await authorize(event, 'tickets:read')
// Account-level routes never accept bearer credentials:
const owner = await requireAuth(event, { sessionOnly: true })
requireAuth,getAuthare auto-imported in server code;@frigear-nu/usersaddsrequireUser/getUserfor the user alone.- Modules register permissions with
addPermission, named scopes withaddScope, default roles withaddRole, policies withaddPoliciesand credential types withaddAuthResolver(@frigear-nu/auth/utils). Server code checks access withauthorize/allows(auto-imported), refines it per record withdefinePolicy, and the host overrides it in theauthorization:beforehook. - Auth knows users only through an
AuthUserStore, which@frigear-nu/usersregisters (setAuthUserStore);@frigear-nu/oauth-serversupplies the access-token resolver. - Sign-in is configured under
authentication.settings(password,magicLink,webAuthn,oauth,pages, ...). - Any nuxt-auth-utils OAuth provider (Auth0, Okta, Keycloak,
oidc, ...) signs in at/api/auth/<provider>once itsNUXT_OAUTH_<PROVIDER>_*credentials are set;settings.oauth.<provider>only overrides its label, icon and sign-up. - The config key is
authentication(authbelongs to nuxt-auth-utils). - The same client API as client mode:
useUserSession(),useAuth()(logingoes to the login page with?redirect=,logoutends the session) and$api/useApi().$apisends the caller's credentials (the incoming Cookie and Authorization headers during a server render) only to this app's own paths and origin, never to another URL or a foreignbaseURL. A 401 from this app while signed in clears the session and goes to the login page. An app must not provide its own$api.
Client mode
authentication.mode: 'client' turns Auth into a public OAuth 2.1 / OpenID Connect client (PKCE) for SPAs and
Capacitor apps that sign in at an issuer: no server, no database, no nuxt-auth-utils, and ssr: false. Set the mode
in the app's own config.
export default defineNuxtConfig({
modules: ['@nuxt/ui', '@frigear-nu/auth'],
authentication: {
mode: 'client',
// Issuer and API also from NUXT_PUBLIC_AUTHENTICATION_CLIENT_ISSUER / _API_BASE.
client: { issuer: 'https://frigear.nu', clientId: 'my-app', scopes: ['openid', 'profile', 'offline_access'] },
},
})
useUserSession()as in server mode (loggedIn,user, withuser.idthe issuer'ssub), plususeAuth()(login,completeLogin,logout,getAccessToken,signingIn, ...) and$api/useApi(), which send the bearer token toclient.apiBaseandclient.apiOriginsonly.scopesmust containopenid: the user comes from the id_token, and a sign-in without one fails.- The user is
{ id: sub, sub, name, email, avatar }from the claims (roles are not read from tokens: with@frigear-nu/authorization,useAuthorization()asks the issuer's/api/auth/me/accesswith the bearer token, or falls back to the access token'spermissionsclaim; UI only, the API decides).client.userinfo: truemerges/userinfo;client.mapUser: './app/auth/map-user'maps it yourself (({ claims, userinfo, api }) => user, may be async). Until an async mapping finishes, and when it fails offline,useris the claims mapping. - Login and callback pages (with
@nuxt/ui) atsettings.pages.loginandclient.callbackPath; branding throughapp.config.ts→authentication.login. - Guards belong to
@frigear-nu/authorizationand are UX only: the API decides. Hooks:auth:signed-in,auth:signed-out,auth:refresh-failed,auth:user-failed. - The framework-agnostic core is
@frigear-nu/auth/client-core, the Capacitor adapters@frigear-nu/auth/capacitor, the types@frigear-nu/auth/client-types;@frigear-nu/auth/modehasresolveAuthModeandisSafeRedirectfor other modules.
See the documentation for the full model, security properties and the token API.
Development
pnpm --filter '@frigear-nu/auth' test
The e2e suite uses test/fixtures/auth-db, whose migration is committed. After changing the schema, regenerate it as
described in the module-kit docs. The client mode's fixtures are test/fixtures/client-*; --project unit runs the
unit tests alone.