An open API service indexing awesome lists of open source software.

https://github.com/gw31415/hono-oidc-simple

Simplifies the implementation of OIDC auth in Hono
https://github.com/gw31415/hono-oidc-simple

honojs oidc

Last synced: about 1 month ago
JSON representation

Simplifies the implementation of OIDC auth in Hono

Awesome Lists containing this project

README

          

# @gw31415/hono-oidc-simple
[![npm version](https://badge.fury.io/js/@gw31415%2Fhono-oidc-simple.svg?icon=si%3Anpm)](https://badge.fury.io/js/@gw31415%2Fhono-oidc-simple)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

`@gw31415/hono-oidc-simple` simplifies the implementation of OpenID Connect
(OIDC) authentication in Hono-based applications. It provides tools for managing
tokens, user sessions, and handling login and logout easily.

---

## Features

- Zero Dependency: No `dependencies` in `package.json`. Only `devDependencies` or
`peerDependencies` (`hono`) are used.
- Middleware Creation: Provides middleware and handlers for managing user
authentication states.
- Customizable: Abstract methods allow flexibility in how tokens are stored or
retrieved.
- Multi-Runtime Support: Works with below runtimes:
- [x] Bun
- [x] Cloudflare Workers
- [x] Deno
- [x] Node.js

## Installation

```bash
npm i @gw31415/hono-oidc-simple
```

## Usage

Please check the example project: [`gw31415/hono-oidc-simple-example`](https://github.com/gw31415/hono-oidc-simple-example).

### Setup and Create Middleware of OIDC

```ts
/** Cookie expiration period */
const COOKIE_MAXAGE = 60 * 60 * 24 * 30 * 6; // 6 months

/** Set-up OIDC */
const oidc = OIDC((c) => {
const envs = env<{
OIDC_GOOGLE_CLIENT: string;
OIDC_GOOGLE_SECRET: string;
}>(c);
return {
issuers: [
{
issuer: "https://accounts.google.com",
authEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
tokenEndpoint: "https://oauth2.googleapis.com/token",
tokenRevocationEndpoint: "https://oauth2.googleapis.com/revoke",
useLocalJwt: false,
createClaims: async (c, tokens) => {
const idToken: string | undefined = await token.getIDToken(c);
if (idToken) {
const jwks = createRemoteJWKSet(
new URL("https://www.googleapis.com/oauth2/v3/certs"),
);
try
{
const { payload } = await jwtVerify(idToken, jwks, {
issuer: "https://accounts.google.com",
audience: envs.OIDC_GOOGLE_CLIENT,
});
return payload as Claims;
} catch (e) {
console.error(e);
}
}
return undefined;
},
scopes: ["openid", "email", "profile"],
client_id: envs.OIDC_GOOGLE_CLIENT,
client_secret: envs.OIDC_GOOGLE_SECRET,
},
],
getIssUrl: () => "https://accounts.google.com",
clientSideTokenStore: {
getRefreshToken: (c) => getCookie(c, "refresh_token"),
getIDToken: (c) => getCookie(c, "jwt"),
setRefreshToken: (c, token) => {
if (!token) {
deleteCookie(c, "refresh_token");
return;
}
const reqUrl = new URL(c.req.url);
const opts: CookieOptions = {
path: "/",
sameSite: "Lax",
httpOnly: true,
secure: reqUrl.hostname !== "localhost",
maxAge: COOKIE_MAXAGE,
};
setCookie(c, "refresh_token", token, opts);
},
setIDToken: (c, token) => {
if (!token) {
deleteCookie(c, "jwt");
return;
}
const reqUrl = new URL(c.req.url);
const secure = reqUrl.hostname !== "localhost";
return setCookie(c, "jwt", token, {
path: "/",
sameSite: "Lax",
httpOnly: true,
secure,
maxAge: COOKIE_MAXAGE,
});
},
},
};
});
```

### Create your Middlewares to get the claims

```ts
type Middleware = OIDCMiddlewareType;

/**
* @param iss OIDC Issuer URL
*/
export const loginRoute = (iss: IssuerType) =>
createRoute(
oidc.loginHandler(iss, (res, c) => {
if (res.type === "ERR") {
const error = res.error;
switch (error) {
case "Unauthorized":
return c.redirect("/");
case "OAuthServerError":
return c.text(`Error: ${error}`, { status: 500 });
default:
return c.text("Invalid state", { status: 500 });
}
}
return c.redirect("/");
}),
);

export const logoutRoute = createRoute(
oidc.logoutHandler((c) => {
return c.redirect("/");
}),
);

export const useClaims = oidc.useClaims;

/** Middleware to specify pages that require login */
export const loginRequired: Middleware = every(useClaims, (async (c, next) => {
if (!c.var.claims) {
return c.render(




Protected Page



You must be logged in to view this page.




Login



,
{ title: "Login Required" },
);
}
return await next();
}) satisfies Middleware);
```

## License

Apache-2.0