Skip to content

Issuer

Reference doc for the OpenAuth server.

The issuer create an OpentAuth server, a Hono app that’s designed to run anywhere.

The issuer function requires a few things:

issuer.ts
import { issuer } from "@openauthjs/openauth"
const app = issuer({
providers: { ... },
storage,
subjects,
success: async (ctx, value) => { ... }
})

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.

issuer.ts
import { GithubProvider } from "@openauthjs/openauth/provider/github"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
const app = issuer({
providers: {
github: GithubAdapter({
// ...
}),
password: PasswordProvider({
// ...
}),
},
})

Handle success

The success callback receives the payload when a user completes a provider’s auth flow.

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
}
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

You define the shape of these in the subjects field.

subjects.ts
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.

issuer.ts
import { subjects } from "./subjects.js"
const app = issuer({
providers: { ... },
subjects,
// ...
})

Deploy

Since issuer is a Hono app, you can deploy it anywhere Hono supports.

issuer.ts
import { serve } from "@hono/node-server"
serve(app)

Methods

issuer

issuer(input)

Parameters

Returns any

Create an OpenAuth server, a Hono app.

IssuerInput

IssuerInput.providers

Type Record<string, Provider>

The providers that you want your OpenAuth server to support.

issuer.ts
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.

issuer.ts
import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo"
issuer({
storage: DynamoStorage()
// ...
})

IssuerInput.subjects

Type SubjectSchema

The shape of the subjects that you want to return.

issuer.ts
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.

issuer.ts
import { THEME_SST } from "@openauthjs/openauth/ui/theme"
issuer({
theme: THEME_SST
// ...
})

Or define your own.

issuer.ts
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 redirectURI is localhost.
  • Compare redirectURI to the request’s hostname or the x-forwarded-host header. 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.

issuer.ts
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

  • 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

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

The given access token is invalid.

InvalidAuthorizationCodeError

The given authorization code is invalid.

InvalidRefreshTokenError

The given refresh token is invalid.

InvalidSubjectError

The given subject is invalid.

MissingParameterError

The given parameter is missing.

MissingProviderError

The provider needs to be passed in.

OauthError

The OAuth server returned an error.

UnauthorizedClientError

The given client is not authorized to use the redirect URI that was passed in.

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.