mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-20 01:35:19 +03:00
feat: Sync to OSS repo
This commit is contained in:
129
packages/auth/src/subject.ts
Normal file
129
packages/auth/src/subject.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Subjects are what the access token generated at the end of the auth flow will map to. Under
|
||||
* the hood, the access token is a JWT that contains this data.
|
||||
*
|
||||
* #### Define subjects
|
||||
*
|
||||
* ```ts title="subjects.ts"
|
||||
* import { object, string } from "valibot"
|
||||
*
|
||||
* const subjects = createSubjects({
|
||||
* user: object({
|
||||
* userID: string()
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* We are using [valibot](https://github.com/fabian-hiller/valibot) here. You can use any
|
||||
* validation library that's following the
|
||||
* [standard-schema specification](https://github.com/standard-schema/standard-schema).
|
||||
*
|
||||
* :::tip
|
||||
* You typically want to place subjects in its own file so it can be imported by all of your apps.
|
||||
* :::
|
||||
*
|
||||
* You can start with one subject. Later you can add more for different types of users.
|
||||
*
|
||||
* #### Set the subjects
|
||||
*
|
||||
* Then you can pass it to the `issuer`.
|
||||
*
|
||||
* ```ts title="issuer.ts"
|
||||
* import { subjects } from "./subjects"
|
||||
*
|
||||
* const app = issuer({
|
||||
* providers: { ... },
|
||||
* subjects,
|
||||
* // ...
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* #### Add the subject payload
|
||||
*
|
||||
* When your user completes the flow, you can add the subject payload in the `success` callback.
|
||||
*
|
||||
* ```ts title="issuer.ts"
|
||||
* const app = issuer({
|
||||
* providers: { ... },
|
||||
* subjects,
|
||||
* async success(ctx, value) {
|
||||
* let userID
|
||||
* if (value.provider === "password") {
|
||||
* console.log(value.email)
|
||||
* userID = ... // lookup user or create them
|
||||
* }
|
||||
* return ctx.subject("user", {
|
||||
* userID
|
||||
* })
|
||||
* },
|
||||
* // ...
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Here we are looking up the userID from our database and adding it to the subject payload.
|
||||
*
|
||||
* :::caution
|
||||
* You should only store properties that won't change for the lifetime of the user.
|
||||
* :::
|
||||
*
|
||||
* Since these will be stored in the access token, you should avoid storing information
|
||||
* that'll change often. For example, if you store the user's username, you'll need to
|
||||
* revoke the access token when the user changes their username.
|
||||
*
|
||||
* #### Decode the subject
|
||||
*
|
||||
* Now when your user logs in, you can use the OpenAuth client to decode the subject. For
|
||||
* example, in our SSR app we can do the following.
|
||||
*
|
||||
* ```ts title="app/page.tsx"
|
||||
* import { subjects } from "../subjects"
|
||||
*
|
||||
* const verified = await client.verify(subjects, cookies.get("access_token")!)
|
||||
* console.log(verified.subject.properties.userID)
|
||||
* ```
|
||||
*
|
||||
* All this is typesafe based on the shape of the subjects you defined.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
import type { v1 } from '@standard-schema/spec';
|
||||
|
||||
import { Prettify } from './util.js';
|
||||
|
||||
/**
|
||||
* Subject schema is a map of types that are used to define the subjects.
|
||||
*/
|
||||
export type SubjectSchema = Record<string, v1.StandardSchema>;
|
||||
|
||||
/** @internal */
|
||||
export type SubjectPayload<T extends SubjectSchema> = Prettify<
|
||||
{
|
||||
[type in keyof T & string]: {
|
||||
type: type;
|
||||
properties: v1.InferOutput<T[type]>;
|
||||
};
|
||||
}[keyof T & string]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Create a subject schema.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const subjects = createSubjects({
|
||||
* user: object({
|
||||
* userID: string()
|
||||
* }),
|
||||
* admin: object({
|
||||
* workspaceID: string()
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* This is using [valibot](https://github.com/fabian-hiller/valibot) to define the shape of the
|
||||
* subjects. You can use any validation library that's following the
|
||||
* [standard-schema specification](https://github.com/standard-schema/standard-schema).
|
||||
*/
|
||||
export function createSubjects<Schema extends SubjectSchema = {}>(types: Schema): Schema {
|
||||
return { ...types };
|
||||
}
|
||||
Reference in New Issue
Block a user