Issuer
The issuer create an OpentAuth server, a Hono app that’s
designed to run anywhere.
The issuer function requires a few things:
import { issuer } from "@openauthjs/openauth"
const app = issuer({ providers: { ... }, storage, subjects, success: async (ctx, value) => { ... }})Add providers
Section titled “Add providers”You start by specifying the auth providers you are going to use. Let’s say you want your users to be able to authenticate with GitHub and with their email and password.
import { GithubProvider } from "@openauthjs/openauth/provider/github"import { PasswordProvider } from "@openauthjs/openauth/provider/password"
const app = issuer({ providers: { github: GithubProvider({ // ... }), password: PasswordProvider({ // ... }), },})Handle success
Section titled “Handle success”The success callback receives the payload when a user completes a provider’s auth flow.
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 } if (value.provider === "github") { console.log(value.tokenset.access) userID = ... // lookup user or create them } return ctx.subject("user", { userID }) }})Once complete, the issuer issues the access tokens that a client can use. The ctx.subject
call is what is placed in the access token as a JWT.
Define subjects
Section titled “Define subjects”You define the shape of these in the subjects field.
import { object, string } from "valibot"import { createSubjects } from "@openauthjs/openauth/subject"
const subjects = createSubjects({ user: object({ userID: string() })})It’s good to place this in a separate file since this’ll be used in your client apps as well.
import { subjects } from "./subjects.js"
const app = issuer({ providers: { ... }, subjects, // ...})Deploy
Section titled “Deploy”Since issuer is a Hono app, you can deploy it anywhere Hono supports.
import { serve } from "@hono/node-server"
serve(app)import { handle } from "hono/aws-lambda"
export const handler = handle(app)export default appexport default appMethods
Section titled “Methods”issuer
Section titled “issuer”issuer(input)Parameters
Section titled “Parameters”-
inputIssuerInput
Returns Hono
Create an OpenAuth server, a Hono app.
IssuerInput
Section titled “IssuerInput”-
providersRecord<string,Provider> -
storage?StorageAdapter -
ttl?Object-
access?number -
refresh?number -
retention?number -
reuse?number
-
-
allow?(input:{audience:string,clientID:string,redirectURI:string}, req:Request) =>Promise<boolean> -
select?(providers:Record<string,string>, req:Request) =>Promise<Response> -
success(response:OnSuccessResponder, input:Result, req:Request) =>Promise<Response>
IssuerInput.providers
Type Record<string, Provider>
The providers that you want your OpenAuth server to support.
import { GithubProvider } from "@openauthjs/openauth/provider/github"
issuer({ providers: { github: GithubProvider() }})The key is just a string that you can use to identify the provider. It’s passed back to
the
success
callback.
You can also specify multiple providers.
{ providers: { github: GithubProvider(), google: GoogleProvider() }}IssuerInput.storage?
Type StorageAdapter
The storage adapter that you want to use.
import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo"
issuer({ storage: DynamoStorage() // ...})IssuerInput.subjects
Type SubjectSchema
The shape of the subjects that you want to return.
import { object, string } from "valibot"import { createSubjects } from "@openauthjs/openauth/subject"
issuer({ subjects: createSubjects({ user: object({ userID: string() }) }) // ...})IssuerInput.theme?
Type Theme
The theme you want to use for the UI.
This includes the UI the user sees when selecting a provider. And the PasswordUI and
CodeUI that are used by the PasswordProvider and CodeProvider.
import { THEME_SST } from "@openauthjs/openauth/ui/theme"
issuer({ theme: THEME_SST // ...})Or define your own.
import type { Theme } from "@openauthjs/openauth/ui/theme"
const MY_THEME: Theme = { // ...}
issuer({ theme: MY_THEME // ...})IssuerInput.ttl?
Type Object
Set the TTL, in seconds, for access and refresh tokens.
{ ttl: { access: 60 * 60 * 24 * 30, refresh: 60 * 60 * 24 * 365 }}IssuerInput.ttl.access?
Type number
Default 30d
Interval in seconds where the access token is valid.
IssuerInput.ttl.refresh?
Type number
Default 1y
Interval in seconds where the refresh token is valid.
IssuerInput.ttl.retention?
Type number
Default 0s
Interval in seconds to retain refresh tokens for reuse detection.
IssuerInput.ttl.reuse?
Type number
Default 60s
Interval in seconds where refresh token reuse is allowed. This helps mitigrate concurrency issues.
IssuerInput.allow?
Type (input: { audience: string, clientID: string, redirectURI: string }, req: Request) => Promise<boolean>
Override the logic for whether a client request is allowed to call the issuer.
By default, it uses the following:
- Allow if the
redirectURIis localhost. - Compare
redirectURIto the request’s hostname or thex-forwarded-hostheader. If they are from the same sub-domain level, then allow.
{ allow: async (input, req) => { // Allow all clients return true }}IssuerInput.select?
Type (providers: Record<string, string>, req: Request) => Promise<Response>
Default Select()
Optionally, configure the UI that’s displayed when the user visits the root URL of the of the OpenAuth server.
import { Select } from "@openauthjs/openauth/ui/select"
issuer({ select: Select({ providers: { github: { hide: true }, google: { display: "Google" } } }) // ...})IssuerInput.success
Type (response: OnSuccessResponder, input: Result, req: Request) => Promise<Response>
The success callback that’s called when the user completes the flow.
This is called after the user has been redirected back to your app after the OAuth flow.
{ success: async (ctx, value) => { let userID if (value.provider === "password") { console.log(value.email) userID = ... // lookup user or create them } if (value.provider === "github") { console.log(value.tokenset.access) userID = ... // lookup user or create them } return ctx.subject("user", { userID }) }, // ...}OnSuccessResponder
Section titled “OnSuccessResponder”-
subject(type:string, properties:any, opts?:{subject:string,ttl:{access:number,refresh:number}}) =>Promise<Response>
Sets the subject payload in the JWT token and returns the response.
ctx.subject("user", { userID})OnSuccessResponder.subject
Type (type: string, properties: any, opts?: { subject: string, ttl: { access: number, refresh: number } }) => Promise<Response>
The type is the type of the subject, that was defined in the subjects field.
The properties are the properties of the subject. This is the shape of the subject that
you defined in the subjects field.
Errors
Section titled “Errors”A list of errors that can be thrown by OpenAuth.
You can use these errors to check the type of error and handle it. For example.
import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error"
if (err instanceof InvalidAuthorizationCodeError) { // handle invalid code error}InvalidAccessTokenError
Section titled “InvalidAccessTokenError”The given access token is invalid.
InvalidAuthorizationCodeError
Section titled “InvalidAuthorizationCodeError”The given authorization code is invalid.
InvalidRefreshTokenError
Section titled “InvalidRefreshTokenError”The given refresh token is invalid.
InvalidSubjectError
Section titled “InvalidSubjectError”The given subject is invalid.
MissingParameterError
Section titled “MissingParameterError”The given parameter is missing.
MissingProviderError
Section titled “MissingProviderError”The provider needs to be passed in.
OauthError
Section titled “OauthError”The OAuth server returned an error.
UnauthorizedClientError
Section titled “UnauthorizedClientError”The given client is not authorized to use the redirect URI that was passed in.
UnknownStateError
Section titled “UnknownStateError”The browser was in an unknown state.
This can happen when certain cookies have expired. Or the browser was switched in the middle of the authentication flow.