diff --git a/docs/content/1.getting-started/3.quick-start.md b/docs/content/1.getting-started/3.quick-start.md index e2be91f0..cb0b1451 100644 --- a/docs/content/1.getting-started/3.quick-start.md +++ b/docs/content/1.getting-started/3.quick-start.md @@ -91,6 +91,53 @@ and return a token that can be used to authenticate future requests in the respo } ``` +### Provider: `refresh` + +The refresh provider does not require any additional steps, as it relies on an already existing backend. By default, the `refresh` provider will try to reach this backend using the following default-configuration: +```ts +{ + baseURL: '/api/auth', + endpoints: { + signIn: { path: '/login', method: 'post' }, + signOut: { path: '/logout', method: 'post' }, + signUp: { path: '/register', method: 'post' }, + getSession: { path: '/session', method: 'get' } + refresh: { path: '/refresh', method: 'post' }, + } +} +``` + +So when you call the `signIn` method, the endpoint `/api/auth/login` will be hit with the `username` and `password` you pass as a body-payload. You likely have to modify these parameters to fit to your backend - you can adjust these parameters in your `nuxt.config.ts` using the options [specified here](/nuxt-auth/v0.6/configuration/nuxt-config). + +Note: The backend can also be in the same Nuxt 3 application, e.g., have a look at this example in the `nuxt-auth` repository: +- [full nuxt app](https://github.com/sidebase/nuxt-auth/tree/main/playground-refresh) + - its [backend](https://github.com/sidebase/nuxt-auth/tree/main/playground-refresh/server/api/auth) + - its [`nuxt.config.ts`](https://github.com/sidebase/nuxt-auth/blob/main/playground-refresh/nuxt.config.ts) + +::alert{type="info"} +The linked example-implementation only serves as a starting-point and is not considered to be secure. +:: + +The backend must accept a request with a body like: +```ts +{ + username: 'bernd@sidebase.io', + password: 'hunter2' +} +``` + +and return a token that can be used to authenticate future requests in the response body, e.g., like: +```ts +{ + tokens: { + accessToken: 'eyBlaBlub' + refreshToken: 'eyBlaubwww' + } +} +``` + +So when you call the `refresh` method, the endpoint `/api/auth/refresh` will be hit with the `refreshToken` you pass as a body-payload. You likely have to modify these parameters to fit to your backend - you can adjust these parameters in your `nuxt.config.ts` using the options [specified here](/nuxt-auth/v0.6/configuration/nuxt-config). + ## Finishing up That's it! You can now use all user-related functionality, for example: @@ -98,13 +145,15 @@ That's it! You can now use all user-related functionality, for example: ::code-group ```ts [Application side] // file: e.g ~/pages/login.vue -const { status, data, signIn, signOut } = useAuth() +const { status, data, signIn, signOut, refresh } = useAuth() status.value // Session status: `unauthenticated`, `loading`, `authenticated` data.value // Session data, e.g., expiration, user.email, ... await signIn() // Sign in the user +await refresh() // Refresh the token await signOut() // Sign out the user + ``` ```ts [authjs: Server side] // file: e.g: ~/server/api/session.get.ts diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md index d79cba6d..187f8824 100644 --- a/docs/content/2.configuration/2.nuxt-config.md +++ b/docs/content/2.configuration/2.nuxt-config.md @@ -228,6 +228,156 @@ type ProviderLocal = { */ sessionDataType?: SessionDataObject, } + +``` +```ts [AuthProviders - refresh] +/** + * Configuration for the `refresh`-provider. + */ +type ProviderRefresh = { + /** + * Uses the `refresh` provider to facilitate autnetication. Currently, two providers exclusive are supported: + * - `authjs`: `next-auth` / `auth.js` based OAuth, Magic URL, Credential provider for non-static applications + * - `local`: Username and password provider with support for static-applications + * - `refresh`: Username and password provider with support for static-applications with refresh token logic + * Read more here: https://sidebase.io/nuxt-auth/v0.6/getting-started + */ + type: Extract + /** + * Endpoints to use for the different methods. `nuxt-auth` will use this and the root-level `baseURL` to create the final request. E.g.: + * - `baseURL=/api/auth`, `path=/login` will result in a request to `/api/auth/login` + * - `baseURL=http://localhost:5000/_authenticate`, `path=/sign-in` will result in a request to `http://localhost:5000/_authenticate/sign-in` + */ + endpoints?: { + /** + * What method and path to call to perform the sign-in. This endpoint must return a token that can be used to authenticate subsequent requests. + * + * @default { path: '/login', method: 'post' } + */ + signIn?: { path?: string, method?: RouterMethod }, + /** + * What method and path to call to perform the sign-out. Set to false to disable. + * + * @default { path: '/logout', method: 'post' } + */ + signOut?: { path?: string, method?: RouterMethod } | false, + /** + * What method and path to call to perform the sign-up. + * + * @default { path: '/register', method: 'post' } + */ + signUp?: { path?: string, method?: RouterMethod }, + /** + * What method and path to call to fetch user / session data from. `nuxt-auth` will send the token received upon sign-in as a header along this request to authenticate. + * + * Refer to the `token` configuration to configure how `nuxt-auth` uses the token in this request. By default it will be send as a bearer-authentication header like so: `Authentication: Bearer eyNDSNJDASNMDSA....` + * + * @default { path: '/session', method: 'get' } + * @example { path: '/user', method: 'get' } + */ + getSession?: { path?: string, method?: RouterMethod }, + /** + * What method and path to call to perform the refresh. + * + * @default { path: '/refresh', method: 'post' } + */ + refresh?: { path?: string, method?: RouterMethod }, + }, + /** + * Pages that `nuxt-auth` needs to know the location off for redirects. + */ + pages?: { + /** + * Path of the login-page that the user should be redirected to, when they try to access a protected page without being logged in. This page will also not be blocked by the global middleware. + * + * @default '/login' + */ + login?: string + }, + /** + * Settings for the authentication-token that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. + */ + token?: { + /** + * How to extract the authentication-token from the sign-in response. + * + * E.g., setting this to `/token/bearer` and returning an object like `{ token: { bearer: 'THE_AUTH_TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will + * result in `nuxt-auth` extracting and storing `THE_AUTH_TOKEN`. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default /token Access the `token` property of the sign-in response object + * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the token + */ + signInResponseTokenPointer?: string + /** + * Header type to be used in requests. This in combination with `headerName` is used to construct the final authentication-header `nuxt-auth` uses, e.g, for requests via `getSession`. + * + * @default Bearer + * @example Beer + */ + type?: string, + /** + * Header name to be used in requests that need to be authenticated, e.g., to be used in the `getSession` request. + * + * @default Authorization + * @example Auth + */ + headerName?: string, + /** + * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. + * + * Note: Your backend may reject / expire the token earlier / differently. + * + * @default 1800 + * @example 60 * 60 * 24 + */ + maxAgeInSeconds?: number, + /** + * The cookie sameSite policy. Can be used as a form of csrf forgery protection. If set to `strict`, the cookie will only be passed with requests to the same 'site'. Typically, this includes subdomains. So, a sameSite: strict cookie set by app.mysite.com will be passed to api.mysite.com, but not api.othersite.com. + * + * See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7 + * + * @default 'lax' + * @example 'strict' + */ + sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, + }, + /** + * Settings for the authentication-refreshToken that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. + */ + refreshToken?: { + /** + * How to extract the authentication-refreshToken from the sign-in response. + * + * E.g., setting this to `/token/refreshToken` and returning an object like `{ token: { refreshToken: 'THE_REFRESH__TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will + * result in `nuxt-auth` extracting and storing `THE_REFRESH__TOKEN`. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default /refreshToken Access the `refreshToken` property of the sign-in response object + * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the refreshToken + */ + signInResponseRefreshTokenPointer?: string + /** + * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. + * + * Note: Your backend may reject / expire the refreshToken earlier / differently. + * + * @default 1800 + * @example 60 * 60 * 24 + */ + maxAgeInSeconds?: number, + }, + /** + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + sessionDataType?: SessionDataObject, +} ``` ```ts [SessionConfig] /** diff --git a/docs/content/3.application-side/2.session-access-and-management.md b/docs/content/3.application-side/2.session-access-and-management.md index 6ecb38a3..232e0838 100644 --- a/docs/content/3.application-side/2.session-access-and-management.md +++ b/docs/content/3.application-side/2.session-access-and-management.md @@ -91,6 +91,56 @@ await signIn(credentials, { callbackUrl: 'https://sidebase.io', external: true } // Trigger a sign-out await signOut() +// Trigger a sign-out and send the user to the sign-out page afterwards +await signOut({ callbackUrl: '/signout' }) +``` +```ts [refresh] +const { + status, + data, + token, + lastRefreshedAt, + getSession, + signUp, + signIn, + signOut, + refresh, + refreshToken +} = useAuth() + +// Session status, either `unauthenticated`, `loading`, `authenticated` +status.value + +// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns +data.value + +// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` +token.value + +// The fetched refreshToken that can be used to token . E.g., a refreshToken like so: `eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` +refreshToken.value + +// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened +lastRefreshedAt.value + +// Get / Reload the current session from the server, pass `{ required: true }` to force a login if no session exists +await getSession() + +// Trigger a sign-in, where `credentials` are the credentials your sign-in endpoint expected, e.g. `{ username: 'bernd', password: 'hunter2' }` +await signIn(credentials) + +// Trigger a sign-in with a redirect afterwards +await signIn(credentials, { callbackUrl: '/protected' }) + +// Trigger a sign-in with a redirect afterwards to an external page (if set, this will cause a hard refresh of the page) +await signIn(credentials, { callbackUrl: 'https://sidebase.io', external: true }) + +// Trigger a refresh, this will set token to new value +await refresh() + +// Trigger a sign-out +await signOut() + // Trigger a sign-out and send the user to the sign-out page afterwards await signOut({ callbackUrl: '/signout' }) ``` @@ -168,6 +218,14 @@ await signIn(credentials, { callbackUrl: '/protected' }) await signOut(credentials, { callbackUrl: '/protected' }) +await getSession(credentials, { callbackUrl: '/protected' }) +``` +```ts [refresh] +const credentials = { username: 'bernd', password: 'hunter2' } +await signIn(credentials, { callbackUrl: '/protected' }) + +await signOut(credentials, { callbackUrl: '/protected' }) + await getSession(credentials, { callbackUrl: '/protected' }) ``` :: @@ -233,6 +291,51 @@ setToken('new token') // Helper method to quickly delete the token cookie (alias for rawToken.value = null) clearToken() ``` + +```ts [refresh] +const { + status, + loading, + data, + lastRefreshedAt, + token, + rawToken, + setToken, + clearToken, + rawRefreshToken, + refreshToken +} = useAuthState() + +// Session status, either `unauthenticated`, `loading`, `authenticated` +status.value + +// Whether any http request is still pending +loading.value + +// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns +data.value + +// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened +lastRefreshedAt.value + +// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` +token.value + +// The fetched refreshToken that can be used to refresh the Token with refresh() methode. +refreshToken.value + +// Cookie that containes the raw fetched token string. This token won't contain any modification or prefixes like `Bearer` or any other. +rawToken.value + +// Cookie that containes the raw fetched refreshToken string. +rawRefreshToken.value + +// Helper method to quickly set a new token (alias for rawToken.value = 'xxx') +setToken('new token') + +// Helper method to quickly delete the token and refresh Token cookie (alias for rawToken.value = null and rawRefreshToken.value = null) +clearToken() +``` :: ::alert{type="warning"} diff --git a/docs/content/3.application-side/3.custom-sign-in-page.md b/docs/content/3.application-side/3.custom-sign-in-page.md index 2100c45c..8b8deff0 100644 --- a/docs/content/3.application-side/3.custom-sign-in-page.md +++ b/docs/content/3.application-side/3.custom-sign-in-page.md @@ -105,9 +105,9 @@ const mySignInHandler = async ({ username, password }: { username: string, passw Then call the `mySignInHandler({ username, password })` on login instead of the default `signIn(...)` method. You can find [all possible errors here](https://github.com/nextauthjs/next-auth/blob/aad0b8db0e8a163b3c3ae7dec3e9158e20d368f4/packages/next-auth/src/core/pages/signin.tsx#L4-L19). This file also contains the default error-messages that `nuxt-auth` would show to the user if you would not handle the error manually using `redirect: false`. -## Provider: `local` +## Provider: `local or refresh` -The only way to use the local provider does not come with a pre-made login page, so you will have to build one yourself. To do so: +The only way to use the local and refresh provider does not come with a pre-made login page, so you will have to build one yourself. To do so: 1. Create the page, e.g., `pages/login.vue` 2. Call the `signIn` method with the username and password your user has to enter on this page 3. Disable the page protection, if you have the global middleware enabled diff --git a/docs/content/4.server-side/4.rest-api.md b/docs/content/4.server-side/4.rest-api.md index d44fd7a8..86a6f6cf 100644 --- a/docs/content/4.server-side/4.rest-api.md +++ b/docs/content/4.server-side/4.rest-api.md @@ -10,6 +10,7 @@ All endpoints that NextAuth.js supports are also supported by `nuxt-auth`: |--------------------------------|:-------------| | `${basePath}/signin` | `GET` | | `${basePath}/signin/:provider` | `POST` | +| `${basePath}/refresh/:provider` | `POST` | | `${basePath}/callback/:provider` | `GET` `POST` | | `${basePath}/signout` | `GET` `POST` | | `${basePath}/session` | `GET` |