Skip to content

Commit

Permalink
Merge pull request #7 from markusahlstrand/silent-auth
Browse files Browse the repository at this point in the history
add silent auth for the code flow
  • Loading branch information
markusahlstrand authored Dec 9, 2024
2 parents 7678f35 + 376d823 commit 294ed22
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 24 deletions.
8 changes: 8 additions & 0 deletions apps/demo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @authhero/demo

## 0.2.6

### Patch Changes

- Updated dependencies
- [email protected]
- @authhero/kysely-adapter@0.24.2

## 0.2.5

### Patch Changes
Expand Down
6 changes: 3 additions & 3 deletions apps/demo/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "@authhero/demo",
"private": true,
"version": "0.2.5",
"version": "0.2.6",
"scripts": {
"dev": "bun --watch src/bun.ts"
},
"dependencies": {
"@authhero/kysely-adapter": "^0.24.1",
"@authhero/kysely-adapter": "^0.24.2",
"@hono/swagger-ui": "^0.4.1",
"@hono/zod-openapi": "^0.18.0",
"authhero": "^0.16.0",
"authhero": "^0.17.0",
"hono": "^4.6.11",
"hono-openapi-middlewares": "^1.0.11",
"kysely-bun-sqlite": "^0.3.2"
Expand Down
6 changes: 6 additions & 0 deletions packages/adapter-interfaces/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @authhero/adapter-interfaces

## 0.29.0

### Minor Changes

- add silent tokens

## 0.28.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-interfaces/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"type": "git",
"url": "https://github.com/markusahlstrand/authhero"
},
"version": "0.28.0",
"version": "0.29.0",
"files": [
"dist"
],
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-interfaces/src/types/AuthParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export enum AuthorizationResponseMode {
}

export enum CodeChallengeMethod {
S265 = "S256",
plain = "plain",
S256 = "S256",
Plain = "plain",
}

export const authParamsSchema = z.object({
Expand Down
11 changes: 11 additions & 0 deletions packages/authhero/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# authhero

## 0.17.0

### Minor Changes

- add silent tokens

### Patch Changes

- Updated dependencies
- @authhero/adapter-interfaces@0.29.0

## 0.16.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/authhero/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "authhero",
"version": "0.16.0",
"version": "0.17.0",
"files": [
"dist"
],
Expand Down
34 changes: 33 additions & 1 deletion packages/authhero/src/authentication-flows/authorization-code.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { HTTPException } from "hono/http-exception";
import { Context } from "hono";
import { z } from "@hono/zod-openapi";
import { nanoid } from "nanoid";
import { createAuthTokens } from "./common";
import { Bindings, Variables } from "../types";
import { computeCodeChallenge } from "../utils/crypto";
import { SILENT_AUTH_MAX_AGE, SILENT_COOKIE_NAME } from "../constants";
import { serializeCookie } from "oslo/cookie";

export const authorizationCodeGrantParamsSchema = z
.object({
Expand Down Expand Up @@ -97,5 +100,34 @@ export async function authorizationCodeGrant(

await ctx.env.data.codes.remove(client.tenant.id, params.code);

return createAuthTokens(ctx, { authParams: login.authParams, user });
// Create a new session
const session = await ctx.env.data.sessions.create(client.tenant.id, {
session_id: nanoid(),
user_id: user.user_id,
client_id: client.id,
expires_at: new Date(Date.now() + SILENT_AUTH_MAX_AGE * 1000).toISOString(),
used_at: new Date().toISOString(),
});

const tokens = await createAuthTokens(ctx, {
authParams: login.authParams,
user,
sid: session.session_id,
});

return ctx.json(tokens, {
headers: {
"set-cookie": serializeCookie(
`${client.tenant.id}-${SILENT_COOKIE_NAME}`,
session.session_id,
{
path: "/",
httpOnly: true,
secure: true,
maxAge: 60 * 60 * 24 * 7, // 1 mo
sameSite: "none",
},
),
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ export async function clientCredentialsGrant(
audience: params.audience,
};

return createAuthTokens(ctx, { authParams });
const tokens = await createAuthTokens(ctx, { authParams });
return ctx.json(tokens);
}
1 change: 1 addition & 0 deletions packages/authhero/src/authentication-flows/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export async function createAuthTokens(
sub: user?.user_id || authParams.client_id,
iss: ctx.env.ISSUER,
tenant_id: ctx.var.tenant_id,
sid,
},
{
includeIssuedTimestamp: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/authhero/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const JWKS_CACHE_TIMEOUT_IN_SECONDS = 60 * 5; // 5 minutes
export const SILENT_AUTH_MAX_AGE = 30 * 24 * 60 * 60; // 30 days
export const SILENT_COOKIE_NAME = "auth-token";
17 changes: 7 additions & 10 deletions packages/authhero/src/routes/oauth2/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const tokenRoutes = new OpenAPIHono<{
},
},
}),
// @ts-ignore
async (ctx) => {
const body = ctx.req.valid("form");

Expand All @@ -100,18 +101,14 @@ export const tokenRoutes = new OpenAPIHono<{

switch (body.grant_type) {
case GrantType.AuthorizationCode:
return ctx.json(
await authorizationCodeGrant(
ctx,
authorizationCodeGrantParamsSchema.parse(params),
),
return authorizationCodeGrant(
ctx,
authorizationCodeGrantParamsSchema.parse(params),
);
case GrantType.ClientCredential:
return ctx.json(
await clientCredentialsGrant(
ctx,
clientCredentialGrantParamsSchema.parse(params),
),
return clientCredentialsGrant(
ctx,
clientCredentialGrantParamsSchema.parse(params),
);
default:
throw new HTTPException(400, { message: "Not implemented" });
Expand Down
63 changes: 60 additions & 3 deletions packages/authhero/test/routes/oauth2/token.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { testClient } from "hono/testing";
import { getTestServer } from "../../helpers/test-server";
import { parseJWT } from "oslo/jwt";
import { computeCodeChallenge } from "../../../src/utils/crypto";
import { CodeChallengeMethod } from "@authhero/adapter-interfaces";

describe("token", () => {
describe("client_credentials", () => {
Expand Down Expand Up @@ -446,6 +447,62 @@ describe("token", () => {

expect(secondResponse.status).toBe(403);
});

it("should set a silent authentication token", async () => {
const { oauthApp, env } = await getTestServer();
const client = testClient(oauthApp, env);

// Create the login session and code
const loginSesssion = await env.data.logins.create("tenantId", {
expires_at: new Date(Date.now() + 1000 * 60 * 5).toISOString(),
authParams: {
client_id: "clientId",
username: "[email protected]",
scope: "",
audience: "http://example.com",
redirect_uri: "http://example.com/callback",
},
});

await env.data.codes.create("tenantId", {
code_type: "authorization_code",
user_id: "email|userId",
code_id: "123456",
login_id: loginSesssion.login_id,
expires_at: new Date(Date.now() + 1000 * 60 * 5).toISOString(),
});

const response = await client.oauth.token.$post(
{
form: {
grant_type: "authorization_code",
code: "123456",
redirect_uri: "http://example.com/callback",
client_id: "clientId",
client_secret: "clientSecret",
},
},
{
headers: {
"tenant-id": "tenantId",
},
},
);

expect(response.status).toBe(200);
const body = await response.json();

const accessToken = parseJWT(body.access_token);

if (!accessToken || !("sid" in accessToken.payload)) {
throw new Error("sid is missing");
}

const cookie = response.headers.get("set-cookie");
expect(cookie).toBe(
`tenantId-auth-token=${accessToken?.payload.sid}; HttpOnly; Max-Age=604800; Path=/; SameSite=None; Secure`,
);
});
});

describe("authorization_code with PKCE", () => {
Expand All @@ -465,7 +522,7 @@ describe("token", () => {
scope: "",
audience: "http://example.com",
code_challenge: codeChallenge,
code_challenge_method: "plain",
code_challenge_method: CodeChallengeMethod.Plain,
},
});

Expand Down Expand Up @@ -523,7 +580,7 @@ describe("token", () => {
scope: "",
audience: "http://example.com",
code_challenge: await computeCodeChallenge(codeChallenge, "S256"),
code_challenge_method: "S256",
code_challenge_method: CodeChallengeMethod.S256,
},
});

Expand Down Expand Up @@ -581,7 +638,7 @@ describe("token", () => {
scope: "",
audience: "http://example.com",
code_challenge: codeChallenge,
code_challenge_method: "plain",
code_challenge_method: CodeChallengeMethod.Plain,
},
});

Expand Down
7 changes: 7 additions & 0 deletions packages/drizzle/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @authhero/drizzle

## 0.1.64

### Patch Changes

- Updated dependencies
- @authhero/adapter-interfaces@0.29.0

## 0.1.63

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/drizzle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"type": "git",
"url": "https://github.com/markusahlstrand/authhero"
},
"version": "0.1.63",
"version": "0.1.64",
"files": [
"dist"
],
Expand Down
7 changes: 7 additions & 0 deletions packages/kysely/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @authhero/kysely-adapter

## 0.24.2

### Patch Changes

- Updated dependencies
- @authhero/adapter-interfaces@0.29.0

## 0.24.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/kysely/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"type": "git",
"url": "https://github.com/markusahlstrand/authhero"
},
"version": "0.24.1",
"version": "0.24.2",
"files": [
"dist"
],
Expand Down

0 comments on commit 294ed22

Please sign in to comment.