Elysoid
Minimal, fully typesafe OpenID Elysia plugin for single page apps with
stateless session management with JWT.
Basic setup
bun add elysoid
import { Static, t } from "elysia"
import { IdTokenPayload, auth as authPlugin } from "elysoid"
const SessionSchema = t.Object({
id: t.String(),
roles: t.Array(t.String())
})
type Session = Static<typeof SessionSchema>
const login = async (payload: IdTokenPayload): Promise<Session | null> => {
return {
id: "1",
roles: [ "admin" ]
}
}
export const auth = authPlugin(SessionSchema, login)
import { Elysia, t } from "elysia"
import { AuthenticationError, AuthorizationError } from "elysoid"
import { auth } from "./plugin/auth"
const app = new Elysia()
.use(auth)
.post("/api/demo", ({ body, user }) => {
if (!user)
throw new AuthenticationError()
if (!user.roles.includes("admin"))
throw new AuthorizationError()
return { msg: `${body.data} received from ${user.id}` }
},
{
body: t.Object({ data: t.String() }),
response: t.Object({ msg: t.String() })
})
.listen(3000)
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
export type App = typeof app
TOKEN_ENDPOINT=https://openidprovider.com/token
CLIENT_ID=yourlientid
CLIENT_SECRET=yourclientsecret
REDIRECT_URI=https://yourapp.com/callback
JWT_SECRET=shhhh
JWT_EXP=2h
LOG_LEVEL=trace
AUTH_LOG_LEVEL=trace
import { edenTreaty } from '@elysiajs/eden'
import type { App } from '../../api/src/index'
const app = edenTreaty<App>('http://localhost:3000')
const login = async () => {
const response = await app['social-login'].post({
code: "<from query param>"
})
}
And that's it.
Advanced setup
You might want to connect to multiple providers, or configure certain other
aspects of the plugin. With the typed config it is pretty self-explanatory, here
is a full example nonetheless.
import { Static, t } from "elysia"
import { IdTokenPayload, multiProviderAuth as authPlugin } from "elysoid"
const SessionSchema = t.Object({
id: t.String(),
roles: t.Array(t.String())
})
type Session = Static<typeof SessionSchema>
const login = async (payload: IdTokenPayload, provider: string): Promise<Session | null> => {
return {
id: "1",
roles: [ "admin" ]
}
}
export const auth = authPlugin(SessionSchema, login, {
session: {
authHeader: "authorization",
tokenPrefix: "Bearer ",
},
jwt: {
secret: "shhh",
expiration: "2h",
},
providers: {
'google': {
tokenEndpoint: "https://google.com/token",
clientId: "yourgoogleclientid",
clientSecret: "yourgoogleclientsecret",
redirectUri: "https://yourapp.com/googlecb"
},
'apple': {
tokenEndpoint: "https://apple.com/token",
clientId: "yourappleclientid",
clientSecret: "yourappleclientsecret",
redirectUri: "https://yourapp.com/applecb"
},
}
}, (logMessage) => console.log(logMessage))
import { edenTreaty } from '@elysiajs/eden'
import type { App } from '../../api/src/index'
const app = edenTreaty<App>('http://localhost:3000')
const login = async () => {
const response = await app['social-login'].post({
code: "<from query param>",
provider: "google"
})
}