From 989578c246f5172a0473d275d834f27378ef9075 Mon Sep 17 00:00:00 2001 From: Markus Ahlstrand Date: Tue, 31 Dec 2024 13:34:13 +0100 Subject: [PATCH 1/3] use default listparams --- apps/demo/CHANGELOG.md | 8 ++++++++ apps/demo/package.json | 6 +++--- packages/adapter-interfaces/CHANGELOG.md | 6 ++++++ packages/adapter-interfaces/package.json | 2 +- .../src/adapters/Applications.ts | 2 +- .../adapter-interfaces/src/adapters/Codes.ts | 2 +- .../src/adapters/Connections.ts | 5 ++++- .../src/adapters/Domains.ts | 2 +- .../adapter-interfaces/src/adapters/Hooks.ts | 2 +- .../adapter-interfaces/src/adapters/Logs.ts | 2 +- .../src/adapters/Sessions.ts | 2 +- .../src/adapters/Tenants.ts | 2 +- .../adapter-interfaces/src/adapters/Users.ts | 2 +- packages/authhero/CHANGELOG.md | 7 +++++++ packages/authhero/package.json | 2 +- packages/drizzle/CHANGELOG.md | 7 +++++++ packages/drizzle/package.json | 2 +- packages/kysely/CHANGELOG.md | 11 ++++++++++ packages/kysely/package.json | 2 +- packages/kysely/src/codes/list.ts | 6 +++++- packages/kysely/src/connections/list.ts | 6 +++++- packages/kysely/src/hooks/list.ts | 6 +++++- packages/kysely/src/sessions/list.ts | 6 +++++- .../tenants/{createTenant.ts => create.ts} | 2 +- .../src/tenants/{getTenant.ts => get.ts} | 2 +- packages/kysely/src/tenants/index.ts | 20 +++++++++---------- .../src/tenants/{listTenants.ts => list.ts} | 2 +- .../tenants/{removeTenant.ts => remove.ts} | 2 +- .../tenants/{updateTenant.ts => update.ts} | 2 +- packages/kysely/src/users/index.ts | 4 ++-- packages/kysely/src/users/list.ts | 8 ++++++-- 31 files changed, 101 insertions(+), 39 deletions(-) rename packages/kysely/src/tenants/{createTenant.ts => create.ts} (90%) rename packages/kysely/src/tenants/{getTenant.ts => get.ts} (90%) rename packages/kysely/src/tenants/{listTenants.ts => list.ts} (95%) rename packages/kysely/src/tenants/{removeTenant.ts => remove.ts} (84%) rename packages/kysely/src/tenants/{updateTenant.ts => update.ts} (89%) diff --git a/apps/demo/CHANGELOG.md b/apps/demo/CHANGELOG.md index a9b53f6..8a19632 100644 --- a/apps/demo/CHANGELOG.md +++ b/apps/demo/CHANGELOG.md @@ -1,5 +1,13 @@ # @authhero/demo +## 0.5.16 + +### Patch Changes + +- Updated dependencies + - @authhero/kysely-adapter@0.28.0 + - authhero@0.36.1 + ## 0.5.15 ### Patch Changes diff --git a/apps/demo/package.json b/apps/demo/package.json index f3b0637..b7691d7 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -1,16 +1,16 @@ { "name": "@authhero/demo", "private": true, - "version": "0.5.15", + "version": "0.5.16", "scripts": { "dev": "bun --watch src/bun.ts" }, "dependencies": { - "@authhero/kysely-adapter": "^0.27.0", + "@authhero/kysely-adapter": "^0.28.0", "@hono/swagger-ui": "^0.5.0", "@hono/zod-openapi": "^0.18.3", "@peculiar/x509": "^1.12.3", - "authhero": "^0.36.0", + "authhero": "^0.36.1", "hono": "^4.6.13", "hono-openapi-middlewares": "^1.0.11", "kysely-bun-sqlite": "^0.3.2", diff --git a/packages/adapter-interfaces/CHANGELOG.md b/packages/adapter-interfaces/CHANGELOG.md index a1e644d..c64943a 100644 --- a/packages/adapter-interfaces/CHANGELOG.md +++ b/packages/adapter-interfaces/CHANGELOG.md @@ -1,5 +1,11 @@ # @authhero/adapter-interfaces +## 0.36.0 + +### Minor Changes + +- use default listparams + ## 0.35.0 ### Minor Changes diff --git a/packages/adapter-interfaces/package.json b/packages/adapter-interfaces/package.json index 806e974..b610b20 100644 --- a/packages/adapter-interfaces/package.json +++ b/packages/adapter-interfaces/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "https://github.com/markusahlstrand/authhero" }, - "version": "0.35.0", + "version": "0.36.0", "files": [ "dist" ], diff --git a/packages/adapter-interfaces/src/adapters/Applications.ts b/packages/adapter-interfaces/src/adapters/Applications.ts index 0a34b30..b743f04 100644 --- a/packages/adapter-interfaces/src/adapters/Applications.ts +++ b/packages/adapter-interfaces/src/adapters/Applications.ts @@ -7,7 +7,7 @@ export interface ApplicationsAdapter { remove(tenant_id: string, id: string): Promise; list( tenant_id: string, - params: ListParams, + params?: ListParams, ): Promise<{ applications: Application[]; totals?: Totals }>; update( tenant_id: string, diff --git a/packages/adapter-interfaces/src/adapters/Codes.ts b/packages/adapter-interfaces/src/adapters/Codes.ts index 0056b98..0514ede 100644 --- a/packages/adapter-interfaces/src/adapters/Codes.ts +++ b/packages/adapter-interfaces/src/adapters/Codes.ts @@ -12,7 +12,7 @@ export interface CodesAdapter { code_id: string, type: CodeType, ) => Promise; - list: (tenant_id: string, params: ListParams) => Promise; + list: (tenant_id: string, params?: ListParams) => Promise; used: (tenant_id: string, code_id: string) => Promise; remove: (tenant_id: string, code_id: string) => Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Connections.ts b/packages/adapter-interfaces/src/adapters/Connections.ts index 3e2ad19..c079187 100644 --- a/packages/adapter-interfaces/src/adapters/Connections.ts +++ b/packages/adapter-interfaces/src/adapters/Connections.ts @@ -14,5 +14,8 @@ export interface ConnectionsAdapter { connection_id: string, params: Partial, ): Promise; - list(tenant_id: string, params: ListParams): Promise; + list( + tenant_id: string, + params?: ListParams, + ): Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Domains.ts b/packages/adapter-interfaces/src/adapters/Domains.ts index 79031d3..82f7fe4 100644 --- a/packages/adapter-interfaces/src/adapters/Domains.ts +++ b/packages/adapter-interfaces/src/adapters/Domains.ts @@ -7,5 +7,5 @@ interface ListDomainsResponse extends Totals { export interface DomainsAdapter { create(tenant_id: string, params: Domain): Promise; - list(tenant_id: string, params: ListParams): Promise; + list(tenant_id: string, params?: ListParams): Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Hooks.ts b/packages/adapter-interfaces/src/adapters/Hooks.ts index 15f2a6a..d19dcf9 100644 --- a/packages/adapter-interfaces/src/adapters/Hooks.ts +++ b/packages/adapter-interfaces/src/adapters/Hooks.ts @@ -14,5 +14,5 @@ export interface HooksAdapter { hook_id: string, hook: Partial, ) => Promise; - list: (tenant_id: string, params: ListParams) => Promise; + list: (tenant_id: string, params?: ListParams) => Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Logs.ts b/packages/adapter-interfaces/src/adapters/Logs.ts index 0bf1192..4a17816 100644 --- a/packages/adapter-interfaces/src/adapters/Logs.ts +++ b/packages/adapter-interfaces/src/adapters/Logs.ts @@ -7,6 +7,6 @@ interface ListLogsResponse extends Totals { export interface LogsDataAdapter { create(tenantId: string, params: Log): Promise; - list(tenantId: string, params: ListParams): Promise; + list(tenantId: string, params?: ListParams): Promise; get(tenantId: string, logId: string): Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Sessions.ts b/packages/adapter-interfaces/src/adapters/Sessions.ts index 53b42c5..d6d8d8b 100644 --- a/packages/adapter-interfaces/src/adapters/Sessions.ts +++ b/packages/adapter-interfaces/src/adapters/Sessions.ts @@ -8,7 +8,7 @@ export interface ListSesssionsResponse extends Totals { export interface SessionsAdapter { create: (tenant_id: string, session: SessionInsert) => Promise; get: (tenant_id: string, id: string) => Promise; - list(tenantId: string, params: ListParams): Promise; + list(tenantId: string, params?: ListParams): Promise; update: ( tenant_id: string, id: string, diff --git a/packages/adapter-interfaces/src/adapters/Tenants.ts b/packages/adapter-interfaces/src/adapters/Tenants.ts index 33ed075..1e23dcd 100644 --- a/packages/adapter-interfaces/src/adapters/Tenants.ts +++ b/packages/adapter-interfaces/src/adapters/Tenants.ts @@ -12,7 +12,7 @@ export interface CreateTenantParams { export interface TenantsDataAdapter { create(params: CreateTenantParams): Promise; get(id: string): Promise; - list(params: ListParams): Promise<{ tenants: Tenant[]; totals?: Totals }>; + list(params?: ListParams): Promise<{ tenants: Tenant[]; totals?: Totals }>; update(id: string, tenant: Partial): Promise; remove(tenantId: string): Promise; } diff --git a/packages/adapter-interfaces/src/adapters/Users.ts b/packages/adapter-interfaces/src/adapters/Users.ts index f467b72..6060696 100644 --- a/packages/adapter-interfaces/src/adapters/Users.ts +++ b/packages/adapter-interfaces/src/adapters/Users.ts @@ -9,7 +9,7 @@ export interface UserDataAdapter { get(tenant_id: string, id: string): Promise; create(tenantId: string, user: UserInsert): Promise; remove(tenantId: string, id: string): Promise; - list(tenantId: string, params: ListParams): Promise; + list(tenantId: string, params?: ListParams): Promise; update(tenantId: string, id: string, user: Partial): Promise; unlink( tenantId: string, diff --git a/packages/authhero/CHANGELOG.md b/packages/authhero/CHANGELOG.md index d774b47..b1c8348 100644 --- a/packages/authhero/CHANGELOG.md +++ b/packages/authhero/CHANGELOG.md @@ -1,5 +1,12 @@ # authhero +## 0.36.1 + +### Patch Changes + +- Updated dependencies + - @authhero/adapter-interfaces@0.36.0 + ## 0.36.0 ### Minor Changes diff --git a/packages/authhero/package.json b/packages/authhero/package.json index b50ba56..d276db8 100644 --- a/packages/authhero/package.json +++ b/packages/authhero/package.json @@ -1,6 +1,6 @@ { "name": "authhero", - "version": "0.36.0", + "version": "0.36.1", "files": [ "dist" ], diff --git a/packages/drizzle/CHANGELOG.md b/packages/drizzle/CHANGELOG.md index fe884c6..602e61c 100644 --- a/packages/drizzle/CHANGELOG.md +++ b/packages/drizzle/CHANGELOG.md @@ -1,5 +1,12 @@ # @authhero/drizzle +## 0.1.73 + +### Patch Changes + +- Updated dependencies + - @authhero/adapter-interfaces@0.36.0 + ## 0.1.72 ### Patch Changes diff --git a/packages/drizzle/package.json b/packages/drizzle/package.json index 47c2e88..802d517 100644 --- a/packages/drizzle/package.json +++ b/packages/drizzle/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "https://github.com/markusahlstrand/authhero" }, - "version": "0.1.72", + "version": "0.1.73", "files": [ "dist" ], diff --git a/packages/kysely/CHANGELOG.md b/packages/kysely/CHANGELOG.md index e7c6910..d2ef58e 100644 --- a/packages/kysely/CHANGELOG.md +++ b/packages/kysely/CHANGELOG.md @@ -1,5 +1,16 @@ # @authhero/kysely-adapter +## 0.28.0 + +### Minor Changes + +- use default listparams + +### Patch Changes + +- Updated dependencies + - @authhero/adapter-interfaces@0.36.0 + ## 0.27.0 ### Minor Changes diff --git a/packages/kysely/package.json b/packages/kysely/package.json index 612e9ce..40608c3 100644 --- a/packages/kysely/package.json +++ b/packages/kysely/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "https://github.com/markusahlstrand/authhero" }, - "version": "0.27.0", + "version": "0.28.0", "files": [ "dist" ], diff --git a/packages/kysely/src/codes/list.ts b/packages/kysely/src/codes/list.ts index f482da2..dfac904 100644 --- a/packages/kysely/src/codes/list.ts +++ b/packages/kysely/src/codes/list.ts @@ -12,7 +12,11 @@ import getCountAsInt from "../utils/getCountAsInt"; export function list(db: Kysely) { return async ( tenant_id: string, - params: ListParams, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, ): Promise => { let query = db.selectFrom("codes").where("codes.tenant_id", "=", tenant_id); diff --git a/packages/kysely/src/connections/list.ts b/packages/kysely/src/connections/list.ts index 3c202fc..1ff9f6e 100644 --- a/packages/kysely/src/connections/list.ts +++ b/packages/kysely/src/connections/list.ts @@ -12,7 +12,11 @@ import getCountAsInt from "../utils/getCountAsInt"; export function list(db: Kysely) { return async ( tenantId: string, - params: ListParams, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, ): Promise => { let query = db .selectFrom("connections") diff --git a/packages/kysely/src/hooks/list.ts b/packages/kysely/src/hooks/list.ts index 2ed144b..feadeb9 100644 --- a/packages/kysely/src/hooks/list.ts +++ b/packages/kysely/src/hooks/list.ts @@ -8,7 +8,11 @@ import getCountAsInt from "../utils/getCountAsInt"; export function list(db: Kysely) { return async ( tenant_id: string, - params: ListParams, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, ): Promise => { let query = db.selectFrom("hooks").where("hooks.tenant_id", "=", tenant_id); diff --git a/packages/kysely/src/sessions/list.ts b/packages/kysely/src/sessions/list.ts index 365f2bb..2eb17fd 100644 --- a/packages/kysely/src/sessions/list.ts +++ b/packages/kysely/src/sessions/list.ts @@ -10,7 +10,11 @@ import getCountAsInt from "../utils/getCountAsInt"; export function list(db: Kysely) { return async ( tenant_id: string, - params: ListParams, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, ): Promise => { let query = db .selectFrom("sessions") diff --git a/packages/kysely/src/tenants/createTenant.ts b/packages/kysely/src/tenants/create.ts similarity index 90% rename from packages/kysely/src/tenants/createTenant.ts rename to packages/kysely/src/tenants/create.ts index 26cbdfc..9adce80 100644 --- a/packages/kysely/src/tenants/createTenant.ts +++ b/packages/kysely/src/tenants/create.ts @@ -3,7 +3,7 @@ import { nanoid } from "nanoid"; import { CreateTenantParams, Tenant } from "@authhero/adapter-interfaces"; import { Database } from "../db"; -export function createTenant(db: Kysely) { +export function create(db: Kysely) { return async (params: CreateTenantParams): Promise => { const tenant: Tenant = { id: params.id || nanoid(), diff --git a/packages/kysely/src/tenants/getTenant.ts b/packages/kysely/src/tenants/get.ts similarity index 90% rename from packages/kysely/src/tenants/getTenant.ts rename to packages/kysely/src/tenants/get.ts index 37f6222..5d0090f 100644 --- a/packages/kysely/src/tenants/getTenant.ts +++ b/packages/kysely/src/tenants/get.ts @@ -3,7 +3,7 @@ import { removeNullProperties } from "../helpers/remove-nulls"; import { Tenant } from "@authhero/adapter-interfaces"; import { Database } from "../db"; -export function getTenant(db: Kysely) { +export function get(db: Kysely) { return async (id: string): Promise => { const tenant = await db .selectFrom("tenants") diff --git a/packages/kysely/src/tenants/index.ts b/packages/kysely/src/tenants/index.ts index 4b9d521..d633275 100644 --- a/packages/kysely/src/tenants/index.ts +++ b/packages/kysely/src/tenants/index.ts @@ -1,18 +1,18 @@ import { Kysely } from "kysely"; -import { createTenant } from "./createTenant"; -import { getTenant } from "./getTenant"; -import { listTenants } from "./listTenants"; -import { updateTenant } from "./updateTenant"; -import { removeTenant } from "./removeTenant"; +import { create } from "./create"; +import { get } from "./get"; +import { list } from "./list"; +import { update } from "./update"; +import { remove } from "./remove"; import { TenantsDataAdapter } from "@authhero/adapter-interfaces"; import { Database } from "../db"; export function createTenantsAdapter(db: Kysely): TenantsDataAdapter { return { - create: createTenant(db), - get: getTenant(db), - list: listTenants(db), - update: updateTenant(db), - remove: removeTenant(db), + create: create(db), + get: get(db), + list: list(db), + update: update(db), + remove: remove(db), }; } diff --git a/packages/kysely/src/tenants/listTenants.ts b/packages/kysely/src/tenants/list.ts similarity index 95% rename from packages/kysely/src/tenants/listTenants.ts rename to packages/kysely/src/tenants/list.ts index 5ede7df..8fdc672 100644 --- a/packages/kysely/src/tenants/listTenants.ts +++ b/packages/kysely/src/tenants/list.ts @@ -4,7 +4,7 @@ import { Database } from "../db"; import { ListParams } from "@authhero/adapter-interfaces"; import getCountAsInt from "../utils/getCountAsInt"; -export function listTenants(db: Kysely) { +export function list(db: Kysely) { return async (params: ListParams) => { let query = db.selectFrom("tenants"); diff --git a/packages/kysely/src/tenants/removeTenant.ts b/packages/kysely/src/tenants/remove.ts similarity index 84% rename from packages/kysely/src/tenants/removeTenant.ts rename to packages/kysely/src/tenants/remove.ts index a681454..94fe2ef 100644 --- a/packages/kysely/src/tenants/removeTenant.ts +++ b/packages/kysely/src/tenants/remove.ts @@ -1,7 +1,7 @@ import { Kysely } from "kysely"; import { Database } from "../db"; -export function removeTenant(db: Kysely) { +export function remove(db: Kysely) { return async (tenant_id: string): Promise => { const results = await db .deleteFrom("tenants") diff --git a/packages/kysely/src/tenants/updateTenant.ts b/packages/kysely/src/tenants/update.ts similarity index 89% rename from packages/kysely/src/tenants/updateTenant.ts rename to packages/kysely/src/tenants/update.ts index efe26e2..e9e500e 100644 --- a/packages/kysely/src/tenants/updateTenant.ts +++ b/packages/kysely/src/tenants/update.ts @@ -2,7 +2,7 @@ import { Tenant } from "@authhero/adapter-interfaces"; import { Kysely } from "kysely"; import { Database } from "../db"; -export function updateTenant(db: Kysely) { +export function update(db: Kysely) { return async (id: string, tenant: Partial): Promise => { const tenantWithModified = { ...tenant, diff --git a/packages/kysely/src/users/index.ts b/packages/kysely/src/users/index.ts index 401abcf..a8765ac 100644 --- a/packages/kysely/src/users/index.ts +++ b/packages/kysely/src/users/index.ts @@ -1,7 +1,7 @@ import { Kysely } from "kysely"; import { create } from "./create"; import { get } from "./get"; -import { listUsers } from "./list"; +import { list } from "./list"; import { remove } from "./remove"; import { update } from "./update"; import { unlink } from "./unlink"; @@ -13,7 +13,7 @@ export function createUsersAdapter(db: Kysely): UserDataAdapter { create: create(db), remove: remove(db), get: get(db), - list: listUsers(db), + list: list(db), update: update(db), // TODO - think about this more when other issues fixed unlink: unlink(db), diff --git a/packages/kysely/src/users/list.ts b/packages/kysely/src/users/list.ts index 39bcc43..b6bdc4a 100644 --- a/packages/kysely/src/users/list.ts +++ b/packages/kysely/src/users/list.ts @@ -10,10 +10,14 @@ import { } from "@authhero/adapter-interfaces"; import getCountAsInt from "../utils/getCountAsInt"; -export function listUsers(db: Kysely) { +export function list(db: Kysely) { return async ( tenantId: string, - params: ListParams, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, ): Promise => { let query = db.selectFrom("users").where("users.tenant_id", "=", tenantId); if (params.q) { From 6ed8f3e3c6c42bfcff15218f83c97ba9661b2fad Mon Sep 17 00:00:00 2001 From: Markus Ahlstrand Date: Wed, 1 Jan 2025 12:23:26 +0100 Subject: [PATCH 2/3] remove list params where not needed --- packages/authhero/CHANGELOG.md | 6 +++ packages/authhero/package.json | 2 +- packages/authhero/src/helpers/client.ts | 12 +---- packages/authhero/src/hooks/webhooks.ts | 7 +-- .../test/routes/auth-api/authenticate.spec.ts | 33 ++------------ .../routes/auth-api/dbconnections.spec.ts | 6 +-- .../test/routes/auth-api/logout.spec.ts | 6 +-- .../test/routes/management-api/users.spec.ts | 45 +------------------ packages/kysely/CHANGELOG.md | 6 +++ packages/kysely/package.json | 2 +- packages/kysely/src/logs/list.ts | 9 +++- packages/kysely/src/tenants/list.ts | 8 +++- packages/kysely/test/connections/list.spec.ts | 8 +--- 13 files changed, 42 insertions(+), 108 deletions(-) diff --git a/packages/authhero/CHANGELOG.md b/packages/authhero/CHANGELOG.md index b1c8348..7718f76 100644 --- a/packages/authhero/CHANGELOG.md +++ b/packages/authhero/CHANGELOG.md @@ -1,5 +1,11 @@ # authhero +## 0.36.2 + +### Patch Changes + +- Remove list params where not needed + ## 0.36.1 ### Patch Changes diff --git a/packages/authhero/package.json b/packages/authhero/package.json index d276db8..1e84188 100644 --- a/packages/authhero/package.json +++ b/packages/authhero/package.json @@ -1,6 +1,6 @@ { "name": "authhero", - "version": "0.36.1", + "version": "0.36.2", "files": [ "dist" ], diff --git a/packages/authhero/src/helpers/client.ts b/packages/authhero/src/helpers/client.ts index a44f8c6..0e59399 100644 --- a/packages/authhero/src/helpers/client.ts +++ b/packages/authhero/src/helpers/client.ts @@ -16,18 +16,10 @@ export async function getClientWithDefaults( : undefined; // TODO: This is not really correct. The connections are not part of a client, but it will be fixed in a later version - const clientConnections = await env.data.connections.list(client.tenant.id, { - include_totals: false, - page: 0, - per_page: 100, - }); + const clientConnections = await env.data.connections.list(client.tenant.id); const defaultConnections = env.DEFAULT_TENANT_ID - ? await env.data.connections.list(env.DEFAULT_TENANT_ID, { - include_totals: false, - page: 0, - per_page: 100, - }) + ? await env.data.connections.list(env.DEFAULT_TENANT_ID) : { connections: [] }; const connections = clientConnections.connections diff --git a/packages/authhero/src/hooks/webhooks.ts b/packages/authhero/src/hooks/webhooks.ts index 227b6e1..7d1555e 100644 --- a/packages/authhero/src/hooks/webhooks.ts +++ b/packages/authhero/src/hooks/webhooks.ts @@ -37,12 +37,7 @@ export function postUserRegistrationWebhook( data: DataAdapters, ) { return async (tenant_id: string, user: User): Promise => { - const { hooks } = await data.hooks.list(tenant_id, { - q: "trigger_id:post-user-registration", - page: 0, - per_page: 100, - include_totals: false, - }); + const { hooks } = await data.hooks.list(tenant_id); await invokeHooks(ctx, hooks, { tenant_id, diff --git a/packages/authhero/test/routes/auth-api/authenticate.spec.ts b/packages/authhero/test/routes/auth-api/authenticate.spec.ts index 31ed083..5b6f8a6 100644 --- a/packages/authhero/test/routes/auth-api/authenticate.spec.ts +++ b/packages/authhero/test/routes/auth-api/authenticate.spec.ts @@ -18,9 +18,6 @@ describe("authenticate", () => { provider: "auth2", is_social: false, user_id: "auth2|userId", - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - login_count: 0, }); // Set the password await env.data.passwords.create("tenantId", { @@ -50,11 +47,7 @@ describe("authenticate", () => { expect(ticket).toBeTypeOf("object"); // Check the logs - const logsResults = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: true, - }); + const logsResults = await env.data.logs.list("tenantId"); expect(logsResults.logs).toHaveLength(1); const [successfulLoginLog] = logsResults.logs; @@ -80,11 +73,7 @@ describe("authenticate", () => { expect(loginResponse.status).toEqual(403); - const logsResults = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: false, - }); + const logsResults = await env.data.logs.list("tenantId"); expect(logsResults).toHaveLength(1); const [failedLoginLog] = logsResults.logs; @@ -108,9 +97,6 @@ describe("authenticate", () => { provider: "auth2", is_social: false, user_id: "auth2|userId", - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - login_count: 0, }); // Set the password await env.data.passwords.create("tenantId", { @@ -131,11 +117,7 @@ describe("authenticate", () => { expect(loginResponse.status).toEqual(403); - const logsResults = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: false, - }); + const logsResults = await env.data.logs.list("tenantId"); expect(logsResults).toHaveLength(1); const [failedLoginLog] = logsResults.logs; @@ -159,9 +141,6 @@ describe("authenticate", () => { provider: "auth2", is_social: false, user_id: "auth2|userId", - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - login_count: 0, }); // Set the password await env.data.passwords.create("tenantId", { @@ -198,11 +177,7 @@ describe("authenticate", () => { expect(loginResponse.status).toEqual(403); - const logsResults = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: false, - }); + const logsResults = await env.data.logs.list("tenantId"); expect(logsResults).toHaveLength(4); const [failedLoginLog] = logsResults.logs; diff --git a/packages/authhero/test/routes/auth-api/dbconnections.spec.ts b/packages/authhero/test/routes/auth-api/dbconnections.spec.ts index 858d6b3..67d596c 100644 --- a/packages/authhero/test/routes/auth-api/dbconnections.spec.ts +++ b/packages/authhero/test/routes/auth-api/dbconnections.spec.ts @@ -60,11 +60,7 @@ describe("dbconnections", () => { }); expect(createdUser._id).toBeTypeOf("string"); - const logs = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: false, - }); + const logs = await env.data.logs.list("tenantId"); expect(logs.length).toBe(1); const emails = getSentEmails(); diff --git a/packages/authhero/test/routes/auth-api/logout.spec.ts b/packages/authhero/test/routes/auth-api/logout.spec.ts index 74021b5..1eec083 100644 --- a/packages/authhero/test/routes/auth-api/logout.spec.ts +++ b/packages/authhero/test/routes/auth-api/logout.spec.ts @@ -86,11 +86,7 @@ describe("logout", () => { const session = await env.data.sessions.get("tenantId", "sid"); expect(session).toBeNull(); - const logs = await env.data.logs.list("tenantId", { - page: 0, - per_page: 10, - include_totals: false, - }); + const logs = await env.data.logs.list("tenantId"); expect(logs.length).toBe(1); }); diff --git a/packages/authhero/test/routes/management-api/users.spec.ts b/packages/authhero/test/routes/management-api/users.spec.ts index 4fb2092..d6f356c 100644 --- a/packages/authhero/test/routes/management-api/users.spec.ts +++ b/packages/authhero/test/routes/management-api/users.spec.ts @@ -163,9 +163,6 @@ describe("users management API endpoint", () => { email_verified: true, connection: "Username-Password-Authentication", is_social: false, - login_count: 0, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); await env.data.users.create("tenantId", { @@ -175,9 +172,6 @@ describe("users management API endpoint", () => { email_verified: true, connection: "email", is_social: false, - login_count: 0, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), linked_to: "auth2|primaryId", }); @@ -387,24 +381,18 @@ describe("users management API endpoint", () => { user_id: "auth2|userId", email: "foo@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); await env.data.users.create("tenantId", { user_id: "auth2|userId2", email: "foo2@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); const updateUserResponse = await managementClient.users[ @@ -619,12 +607,7 @@ describe("users management API endpoint", () => { ); // inspect the db directly because the GET endpoints don't return linked users - const { users } = await env.data.users.list("tenantId", { - page: 0, - per_page: 10, - include_totals: true, - q: "", - }); + const { users } = await env.data.users.list("tenantId"); expect(users.length).toBe(3); // check we have linked user1 to user2 @@ -648,12 +631,7 @@ describe("users management API endpoint", () => { ); // user1 and user2 are deleted - cascading delete in SQL works (at least in SQLite) - const { users: usersNowDeleted } = await env.data.users.list("tenantId", { - page: 0, - per_page: 10, - include_totals: true, - q: "", - }); + const { users: usersNowDeleted } = await env.data.users.list("tenantId"); expect(usersNowDeleted.length).toBe(1); }); @@ -694,7 +672,6 @@ describe("users management API endpoint", () => { // - pagination! What I've done won't work of course unless we overfetch... it("should return an empty list of users for a tenant", async () => { const { managementApp, oauthApp, env } = await getTestServer(); - const client = testClient(oauthApp, env); const managementClient = testClient(managementApp, env); const token = await getAdminToken(); @@ -935,24 +912,18 @@ describe("users management API endpoint", () => { user_id: "auth2|base-user", email: "base-user@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); // create new code user WITH DIFFERENT EMAIL ADDRESS and link this to the password user env.data.users.create("tenantId", { user_id: "auth2|code-user", email: "code-user@example.com", email_verified: true, - login_count: 0, provider: "email", connection: "email", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), linked_to: "auth2|base-user", }); @@ -1195,24 +1166,18 @@ describe("users management API endpoint", () => { user_id: "auth2|userId1", email: "foo1@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); await env.data.users.create("tenantId", { user_id: "auth2|userId2", email: "foo2@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); const linkUserResponse = await managementClient.users[ @@ -1351,24 +1316,18 @@ describe("users management API endpoint", () => { user_id: "auth2|userId1", email: "foo1@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); await env.data.users.create("tenantId", { user_id: "auth2|userId2", email: "foo2@example.com", email_verified: true, - login_count: 0, provider: "auth2", connection: "Username-Password-Authentication", is_social: false, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), }); const linkUserResponse = await managementClient.users[ diff --git a/packages/kysely/CHANGELOG.md b/packages/kysely/CHANGELOG.md index d2ef58e..6db8bba 100644 --- a/packages/kysely/CHANGELOG.md +++ b/packages/kysely/CHANGELOG.md @@ -1,5 +1,11 @@ # @authhero/kysely-adapter +## 0.28.1 + +### Patch Changes + +- Remove list params where not needed + ## 0.28.0 ### Minor Changes diff --git a/packages/kysely/package.json b/packages/kysely/package.json index 40608c3..9362a6a 100644 --- a/packages/kysely/package.json +++ b/packages/kysely/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "https://github.com/markusahlstrand/authhero" }, - "version": "0.28.0", + "version": "0.28.1", "files": [ "dist" ], diff --git a/packages/kysely/src/logs/list.ts b/packages/kysely/src/logs/list.ts index f3e0db5..efadccc 100644 --- a/packages/kysely/src/logs/list.ts +++ b/packages/kysely/src/logs/list.ts @@ -6,7 +6,14 @@ import { Database } from "../db"; import getCountAsInt from "../utils/getCountAsInt"; export function listLogs(db: Kysely) { - return async (tenant_id: string, params: ListParams) => { + return async ( + tenant_id: string, + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, + ) => { let query = db.selectFrom("logs").where("logs.tenant_id", "=", tenant_id); if (params.q) { diff --git a/packages/kysely/src/tenants/list.ts b/packages/kysely/src/tenants/list.ts index 8fdc672..38641e1 100644 --- a/packages/kysely/src/tenants/list.ts +++ b/packages/kysely/src/tenants/list.ts @@ -5,7 +5,13 @@ import { ListParams } from "@authhero/adapter-interfaces"; import getCountAsInt from "../utils/getCountAsInt"; export function list(db: Kysely) { - return async (params: ListParams) => { + return async ( + params: ListParams = { + page: 0, + per_page: 50, + include_totals: false, + }, + ) => { let query = db.selectFrom("tenants"); if (params.sort && params.sort.sort_by) { diff --git a/packages/kysely/test/connections/list.spec.ts b/packages/kysely/test/connections/list.spec.ts index f83e891..7d82263 100644 --- a/packages/kysely/test/connections/list.spec.ts +++ b/packages/kysely/test/connections/list.spec.ts @@ -24,11 +24,7 @@ describe("connections", () => { }, }); - const connections = await db.connections.list("tenantId", { - include_totals: true, - page: 0, - per_page: 10, - }); + const connections = await db.connections.list("tenantId"); expect(connections).toMatchObject({ connections: [ @@ -43,7 +39,7 @@ describe("connections", () => { }, ], length: 1, - limit: 10, + limit: 50, }); }); }); From 04849075cc1090014efb386e40368bba050baf91 Mon Sep 17 00:00:00 2001 From: Markus Ahlstrand Date: Thu, 2 Jan 2025 10:32:45 +0100 Subject: [PATCH 3/3] migrate the enter-email page --- apps/demo/CHANGELOG.md | 11 + apps/demo/package.json | 9 +- apps/demo/src/app.ts | 28 +-- packages/authhero/CHANGELOG.md | 6 + packages/authhero/package.json | 3 +- packages/authhero/src/auth-app.ts | 52 ----- packages/authhero/src/components/AppLogo.tsx | 24 ++ packages/authhero/src/components/Button.tsx | 68 ++++++ .../src/components/CheckEmailPage.tsx | 56 +++++ .../src/components/DisabledSubmitButton.tsx | 30 +++ .../src/components/EmailValidatedPage.tsx | 42 ++++ .../authhero/src/components/EnterCodePage.tsx | 120 ++++++++++ .../src/components/EnterEmailPage.tsx | 149 ++++++++++++ .../src/components/EnterPasswordPage.tsx | 89 ++++++++ .../authhero/src/components/ErrorMessage.tsx | 9 + packages/authhero/src/components/Footer.tsx | 29 +++ .../src/components/ForgotPasswordPage.tsx | 52 +++++ .../src/components/ForgotPasswordSentPage.tsx | 32 +++ packages/authhero/src/components/Form.tsx | 15 ++ packages/authhero/src/components/GoBack.tsx | 16 ++ .../authhero/src/components/GoogleLogo.tsx | 30 +++ packages/authhero/src/components/Icon.tsx | 26 +++ .../src/components/InvalidSession.tsx | 36 +++ packages/authhero/src/components/Layout.tsx | 137 +++++++++++ packages/authhero/src/components/Message.tsx | 25 ++ .../components/PreSignUpConfirmationPage.tsx | 51 +++++ .../authhero/src/components/PreSignUpPage.tsx | 52 +++++ .../src/components/ResetPasswordPage.tsx | 53 +++++ .../authhero/src/components/SignUpPage.tsx | 67 ++++++ .../authhero/src/components/SocialButton.tsx | 71 ++++++ packages/authhero/src/components/Spinner.tsx | 38 +++ packages/authhero/src/components/Trans.tsx | 40 ++++ .../src/components/UnverifiedEmailPage.tsx | 39 ++++ .../src/components/UserNotFoundPage.tsx | 35 +++ .../authhero/src/components/VippsLogo.tsx | 29 +++ packages/authhero/src/index.ts | 9 +- .../authhero/src/routes/auth-api/index.ts | 59 ++++- .../management-api/index.ts} | 29 ++- .../src/routes/universal-login/common.tsx | 198 ++++++++++++++++ .../routes/universal-login/enter-email.tsx | 216 ++++++++++++++++++ .../src/routes/universal-login/index.ts | 26 +++ packages/authhero/src/types/Bindings.ts | 3 + packages/authhero/test/helpers/test-server.ts | 2 +- packages/authhero/tsconfig.json | 4 +- pnpm-lock.yaml | 95 ++++---- 45 files changed, 2059 insertions(+), 151 deletions(-) delete mode 100644 packages/authhero/src/auth-app.ts create mode 100644 packages/authhero/src/components/AppLogo.tsx create mode 100644 packages/authhero/src/components/Button.tsx create mode 100644 packages/authhero/src/components/CheckEmailPage.tsx create mode 100644 packages/authhero/src/components/DisabledSubmitButton.tsx create mode 100644 packages/authhero/src/components/EmailValidatedPage.tsx create mode 100644 packages/authhero/src/components/EnterCodePage.tsx create mode 100644 packages/authhero/src/components/EnterEmailPage.tsx create mode 100644 packages/authhero/src/components/EnterPasswordPage.tsx create mode 100644 packages/authhero/src/components/ErrorMessage.tsx create mode 100644 packages/authhero/src/components/Footer.tsx create mode 100644 packages/authhero/src/components/ForgotPasswordPage.tsx create mode 100644 packages/authhero/src/components/ForgotPasswordSentPage.tsx create mode 100644 packages/authhero/src/components/Form.tsx create mode 100644 packages/authhero/src/components/GoBack.tsx create mode 100644 packages/authhero/src/components/GoogleLogo.tsx create mode 100644 packages/authhero/src/components/Icon.tsx create mode 100644 packages/authhero/src/components/InvalidSession.tsx create mode 100644 packages/authhero/src/components/Layout.tsx create mode 100644 packages/authhero/src/components/Message.tsx create mode 100644 packages/authhero/src/components/PreSignUpConfirmationPage.tsx create mode 100644 packages/authhero/src/components/PreSignUpPage.tsx create mode 100644 packages/authhero/src/components/ResetPasswordPage.tsx create mode 100644 packages/authhero/src/components/SignUpPage.tsx create mode 100644 packages/authhero/src/components/SocialButton.tsx create mode 100644 packages/authhero/src/components/Spinner.tsx create mode 100644 packages/authhero/src/components/Trans.tsx create mode 100644 packages/authhero/src/components/UnverifiedEmailPage.tsx create mode 100644 packages/authhero/src/components/UserNotFoundPage.tsx create mode 100644 packages/authhero/src/components/VippsLogo.tsx rename packages/authhero/src/{management-app.ts => routes/management-api/index.ts} (51%) create mode 100644 packages/authhero/src/routes/universal-login/common.tsx create mode 100644 packages/authhero/src/routes/universal-login/enter-email.tsx create mode 100644 packages/authhero/src/routes/universal-login/index.ts diff --git a/apps/demo/CHANGELOG.md b/apps/demo/CHANGELOG.md index 8a19632..b46c2a3 100644 --- a/apps/demo/CHANGELOG.md +++ b/apps/demo/CHANGELOG.md @@ -1,5 +1,16 @@ # @authhero/demo +## 0.6.0 + +### Minor Changes + +- migrate the enter-email page + +### Patch Changes + +- Updated dependencies + - authhero@0.37.0 + ## 0.5.16 ### Patch Changes diff --git a/apps/demo/package.json b/apps/demo/package.json index b7691d7..ece1bbc 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -1,18 +1,19 @@ { "name": "@authhero/demo", "private": true, - "version": "0.5.16", + "version": "0.6.0", "scripts": { "dev": "bun --watch src/bun.ts" }, "dependencies": { - "@authhero/kysely-adapter": "^0.28.0", + "@authhero/kysely-adapter": "^0.28.1", "@hono/swagger-ui": "^0.5.0", "@hono/zod-openapi": "^0.18.3", "@peculiar/x509": "^1.12.3", - "authhero": "^0.36.1", - "hono": "^4.6.13", + "authhero": "^0.37.0", + "hono": "^4.6.15", "hono-openapi-middlewares": "^1.0.11", + "kysely": "^0.27.4", "kysely-bun-sqlite": "^0.3.2", "oslo": "^1.2.1" } diff --git a/apps/demo/src/app.ts b/apps/demo/src/app.ts index 9dc2e5a..8fce3f8 100644 --- a/apps/demo/src/app.ts +++ b/apps/demo/src/app.ts @@ -8,10 +8,11 @@ import { registerComponent, } from "hono-openapi-middlewares"; import packageJson from "../package.json"; -import { Bindings } from "./types/Bindings"; export default function create(dataAdapter: DataAdapters) { - const app = new OpenAPIHono<{ Bindings: Bindings }>(); + const { app } = init({ + dataAdapter, + }); app .onError((err, ctx) => { @@ -31,29 +32,6 @@ export default function create(dataAdapter: DataAdapters) { }); }) .get("/docs", swaggerUI({ url: "/spec" })); - app.use(createAuthMiddleware(app)); - app.use(registerComponent(app)); - - const { managementApp, oauthApp } = init({ - dataAdapter, - }); - - managementApp.doc("/spec", (c) => ({ - openapi: "3.0.0", - info: { - version: "1.0.0", - title: "Management API", - }, - servers: [ - { - url: new URL(c.req.url).origin, - description: "Current environment", - }, - ], - })); - - app.route("/api/v2", managementApp); - app.route("/", oauthApp); return app; } diff --git a/packages/authhero/CHANGELOG.md b/packages/authhero/CHANGELOG.md index 7718f76..b59b9d3 100644 --- a/packages/authhero/CHANGELOG.md +++ b/packages/authhero/CHANGELOG.md @@ -1,5 +1,11 @@ # authhero +## 0.37.0 + +### Minor Changes + +- migrate the enter-email page + ## 0.36.2 ### Patch Changes diff --git a/packages/authhero/package.json b/packages/authhero/package.json index 1e84188..f941191 100644 --- a/packages/authhero/package.json +++ b/packages/authhero/package.json @@ -1,6 +1,6 @@ { "name": "authhero", - "version": "0.36.2", + "version": "0.37.0", "files": [ "dist" ], @@ -39,6 +39,7 @@ "arctic": "^2.3.3", "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", + "classnames": "^2.5.1", "i18next": "^24.2.0", "nanoid": "^5.0.8", "oslo": "^1.2.1", diff --git a/packages/authhero/src/auth-app.ts b/packages/authhero/src/auth-app.ts deleted file mode 100644 index 11b94ba..0000000 --- a/packages/authhero/src/auth-app.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { OpenAPIHono } from "@hono/zod-openapi"; -import { Bindings, Variables } from "./types"; -import { registerComponent } from "./middlewares/register-component"; -import { DataAdapters } from "@authhero/adapter-interfaces"; -import { createAuthMiddleware } from "./middlewares/authentication"; -import { - logoutRoutes, - tokenRoutes, - wellKnownRoutes, - userinfoRoutes, - dbConnectionRoutes, - passwordlessRoutes, - authenticateRoutes, - authorizeRoutes, -} from "./routes/auth-api"; -import { callbackRoutes } from "./routes/auth-api/callback"; - -export interface CreateAuthParams { - dataAdapter: DataAdapters; -} - -export default function create() { - const app = new OpenAPIHono<{ - Bindings: Bindings; - Variables: Variables; - }>(); - - app.use(createAuthMiddleware(app)); - - const oauthApp = app - .route("/v2/logout", logoutRoutes) - .route("/userinfo", userinfoRoutes) - .route("/.well-known", wellKnownRoutes) - .route("/oauth/token", tokenRoutes) - .route("/dbconnections", dbConnectionRoutes) - .route("/passwordless", passwordlessRoutes) - .route("/co/authenticate", authenticateRoutes) - .route("/authorize", authorizeRoutes) - .route("/callback", callbackRoutes); - - oauthApp.doc("/spec", { - openapi: "3.0.0", - info: { - version: "1.0.0", - title: "Oauth API", - }, - }); - - registerComponent(oauthApp); - - return oauthApp; -} diff --git a/packages/authhero/src/components/AppLogo.tsx b/packages/authhero/src/components/AppLogo.tsx new file mode 100644 index 0000000..d90a759 --- /dev/null +++ b/packages/authhero/src/components/AppLogo.tsx @@ -0,0 +1,24 @@ +import { VendorSettings } from "@authhero/adapter-interfaces"; +import type { FC } from "hono/jsx"; + +type AppLogoProps = { + vendorSettings: VendorSettings; +}; + +const AppLogo: FC = ({ vendorSettings }) => { + if (vendorSettings?.logoUrl) { + return ( +
+ Vendor logo +
+ ); + } + + return <>; +}; + +export default AppLogo; diff --git a/packages/authhero/src/components/Button.tsx b/packages/authhero/src/components/Button.tsx new file mode 100644 index 0000000..01aafc1 --- /dev/null +++ b/packages/authhero/src/components/Button.tsx @@ -0,0 +1,68 @@ +import cn from "classnames"; +import Spinner from "./Spinner"; +import { PropsWithChildren } from "hono/jsx"; + +// in React we would do +// interface Props extends ButtonHTMLAttributes +// to get all the DOM attributes of a button +type Props = { + className?: string; + Component?: string; + variant?: "primary" | "secondary" | "custom"; + // in Nextjs & React we use default DOM element types... + href?: string; + disabled?: boolean; + isLoading?: boolean; + id?: string; +}; + +const Button = ({ + children, + className, + Component = "button", + variant = "primary", + href, + disabled, + isLoading, + id, +}: PropsWithChildren) => { + const hrefProps = Component === "a" ? { href } : {}; + return ( + // @ts-expect-error - refactor this when migrating to authhero + + + {children} + + {isLoading && ( +
+ +
+ )} +
+ ); +}; + +export default Button; diff --git a/packages/authhero/src/components/CheckEmailPage.tsx b/packages/authhero/src/components/CheckEmailPage.tsx new file mode 100644 index 0000000..d4e99ca --- /dev/null +++ b/packages/authhero/src/components/CheckEmailPage.tsx @@ -0,0 +1,56 @@ +import type { FC, JSXNode } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings, User } from "@authhero/adapter-interfaces"; +import i18next, { t } from "i18next"; +import Trans from "./Trans"; +import Form from "./Form"; +import DisabledSubmitButton from "./DisabledSubmitButton"; + +type Props = { + vendorSettings: VendorSettings; + state: string; + user: User; +}; + +const CheckEmailPage: FC = ({ vendorSettings, state, user }) => { + return ( + +
+
+ + ) as unknown as JSXNode, + ]} + values={{ email: user.email }} + /> +
+ {t("continue_with_sso_provider_headline")} +
+ +
+
+ +
+ {i18next.t("yes_continue_with_existing_account")} +
+
+
+ + {i18next.t("no_use_another")} + +
+
+
+ ); +}; + +export default CheckEmailPage; diff --git a/packages/authhero/src/components/DisabledSubmitButton.tsx b/packages/authhero/src/components/DisabledSubmitButton.tsx new file mode 100644 index 0000000..080b570 --- /dev/null +++ b/packages/authhero/src/components/DisabledSubmitButton.tsx @@ -0,0 +1,30 @@ +import Button from "./Button"; +import cn from "classnames"; +import { PropsWithChildren } from "hono/jsx"; + +type Props = { + className?: string; +}; + +const DisabledSubmitButton = ({ + children, + className, +}: PropsWithChildren) => { + return ( + <> + + + + ); +}; + +export default DisabledSubmitButton; diff --git a/packages/authhero/src/components/EmailValidatedPage.tsx b/packages/authhero/src/components/EmailValidatedPage.tsx new file mode 100644 index 0000000..4fdfec9 --- /dev/null +++ b/packages/authhero/src/components/EmailValidatedPage.tsx @@ -0,0 +1,42 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import Button from "./Button"; +import i18next from "i18next"; +import Icon from "./Icon"; +import { VendorSettings } from "authhero"; + +type Props = { + vendorSettings: VendorSettings; + state: string; +}; + +const EmailValidatedPage: FC = ({ vendorSettings, state }) => { + const loginLinkParams = new URLSearchParams({ + state, + }); + + return ( + +
+ {i18next.t("email_validated")} +
+
+ +
+
+ ); +}; + +export default EmailValidatedPage; diff --git a/packages/authhero/src/components/EnterCodePage.tsx b/packages/authhero/src/components/EnterCodePage.tsx new file mode 100644 index 0000000..3765759 --- /dev/null +++ b/packages/authhero/src/components/EnterCodePage.tsx @@ -0,0 +1,120 @@ +import type { FC, JSXNode } from "hono/jsx"; +import Layout from "./Layout"; +import Button from "./Button"; +import i18next from "i18next"; +import cn from "classnames"; +import Icon from "./Icon"; +import ErrorMessage from "./ErrorMessage"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; +import { GoBack } from "./GoBack"; +import { Client, VendorSettings } from "authhero"; +import Trans from "./Trans"; + +type Props = { + error?: string; + vendorSettings: VendorSettings; + email: string; + state: string; + client: Client; + hasPasswordLogin: boolean; +}; + +const CODE_LENGTH = 6; + +const EnterCodePage: FC = ({ + error, + vendorSettings, + email, + state, + client, + hasPasswordLogin, +}) => { + const passwordLoginLinkParams = new URLSearchParams({ + state, + }); + + const connections = client.connections.map(({ name }) => name); + const showPasswordLogin = connections.includes("auth2"); + + return ( + +
+ {i18next.t("verify_your_email")} +
+
+ + ) as unknown as JSXNode, + ]} + values={{ email }} + /> +
+
+
+ + {error && {error}} +
+ +
+ {i18next.t("login")} + +
+
+
+
+ +
+ {i18next.t("sent_code_spam")} +
+
+ {showPasswordLogin && ( +
+
+
+
+ {i18next.t("or")} +
+
+ +
+ )} + + +
+ + ); +}; + +export default EnterCodePage; diff --git a/packages/authhero/src/components/EnterEmailPage.tsx b/packages/authhero/src/components/EnterEmailPage.tsx new file mode 100644 index 0000000..a51272a --- /dev/null +++ b/packages/authhero/src/components/EnterEmailPage.tsx @@ -0,0 +1,149 @@ +import type { FC } from "hono/jsx"; +import { Client, Login, VendorSettings } from "authhero"; +import Layout from "./Layout"; +import i18next from "i18next"; +import cn from "classnames"; +import Icon from "./Icon"; +import ErrorMessage from "./ErrorMessage"; +import SocialButton from "./SocialButton"; +import Google from "./GoogleLogo"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; +import VippsLogo from "./VippsLogo"; + +type Props = { + error?: string; + vendorSettings: VendorSettings; + session: Login; + email?: string; + client: Client; + impersonation?: boolean; +}; + +const EnterEmailPage: FC = ({ + error, + vendorSettings, + session, + email, + client, + impersonation, +}) => { + const connections = client.connections.map(({ name }) => name); + const showFacebook = connections.includes("facebook"); + const showGoogle = connections.includes("google-oauth2"); + const showApple = connections.includes("apple"); + const showVipps = connections.includes("vipps"); + const anySocialLogin = showFacebook || showGoogle || showApple || showVipps; + + return ( + +
+ {i18next.t("welcome")} +
+
{i18next.t("login_description")}
+
+
+ + {impersonation && ( + + )} + {error && {error}} + +
+ {i18next.t("continue")} + +
+
+
+ {anySocialLogin && ( +
+
+
+ {i18next.t("continue_social_login")} +
+
+ )} +
+ {showFacebook && ( + + } + session={session} + /> + )} + {showGoogle && ( + + } + session={session} + /> + )} + {showApple && ( + + } + session={session} + /> + )} + {showVipps && ( + + } + session={session} + /> + )} +
+
+ + ); +}; + +export default EnterEmailPage; diff --git a/packages/authhero/src/components/EnterPasswordPage.tsx b/packages/authhero/src/components/EnterPasswordPage.tsx new file mode 100644 index 0000000..6749ddb --- /dev/null +++ b/packages/authhero/src/components/EnterPasswordPage.tsx @@ -0,0 +1,89 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import Button from "./Button"; +import i18next from "i18next"; +import ErrorMessage from "./ErrorMessage"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Icon from "./Icon"; +import Form from "./Form"; +import { GoBack } from "./GoBack"; +import { Client, VendorSettings } from "authhero"; + +type Props = { + error?: string; + vendorSettings: VendorSettings; + email: string; + state: string; + client: Client; +}; + +const EnterPasswordPage: FC = (params) => { + const { error, vendorSettings, email, state } = params; + + const loginLinkParams = new URLSearchParams({ + state, + }); + + return ( + +
+ {i18next.t("enter_password")} +
+
+ {i18next.t("enter_password_description")} +
+
+
+ + + {error && {error}} + +
+ {i18next.t("login")} + +
+
+
+ + {i18next.t("forgot_password_link")} + +
+
+
+
+ {i18next.t("or")} +
+
+
+ + + +
+
+ +
+ + ); +}; + +export default EnterPasswordPage; diff --git a/packages/authhero/src/components/ErrorMessage.tsx b/packages/authhero/src/components/ErrorMessage.tsx new file mode 100644 index 0000000..3d67d93 --- /dev/null +++ b/packages/authhero/src/components/ErrorMessage.tsx @@ -0,0 +1,9 @@ +type Props = { + children: string; +}; + +const ErrorMessage = ({ children }: Props) => { + return
{children}
; +}; + +export default ErrorMessage; diff --git a/packages/authhero/src/components/Footer.tsx b/packages/authhero/src/components/Footer.tsx new file mode 100644 index 0000000..a7056f1 --- /dev/null +++ b/packages/authhero/src/components/Footer.tsx @@ -0,0 +1,29 @@ +import i18next from "i18next"; +import { VendorSettings } from "authhero"; + +type Props = { + vendorSettings: VendorSettings; +}; +const Footer = ({ vendorSettings }: Props) => { + const { termsAndConditionsUrl } = vendorSettings; + + return ( +
+ {termsAndConditionsUrl && ( +
+ {i18next.t("agree_to")}{" "} + + {i18next.t("terms")} + +
+ )} +
+ ); +}; + +export default Footer; diff --git a/packages/authhero/src/components/ForgotPasswordPage.tsx b/packages/authhero/src/components/ForgotPasswordPage.tsx new file mode 100644 index 0000000..52ed30f --- /dev/null +++ b/packages/authhero/src/components/ForgotPasswordPage.tsx @@ -0,0 +1,52 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import i18next from "i18next"; +import ErrorMessage from "./ErrorMessage"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; +import { GoBack } from "./GoBack"; + +type Props = { + error?: string; + vendorSettings: VendorSettings; + email?: string; + state: string; +}; + +const ForgotPasswordPage: FC = (parms) => { + const { error, vendorSettings, email, state } = parms; + + return ( + +
+ {i18next.t("forgot_password_title")} +
+
+ {i18next.t("forgot_password_description")} +
+
+
+ + {error && {error}} + + {i18next.t("forgot_password_cta")} + +
+ +
+
+ ); +}; + +export default ForgotPasswordPage; diff --git a/packages/authhero/src/components/ForgotPasswordSentPage.tsx b/packages/authhero/src/components/ForgotPasswordSentPage.tsx new file mode 100644 index 0000000..ed5474a --- /dev/null +++ b/packages/authhero/src/components/ForgotPasswordSentPage.tsx @@ -0,0 +1,32 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import { GoBack } from "./GoBack"; +import i18next from "i18next"; +import Icon from "./Icon"; + +type Props = { + vendorSettings: VendorSettings; + state: string; +}; + +const ForgotPasswordSentPage: FC = (params) => { + const { vendorSettings, state } = params; + + return ( + +
+
{i18next.t("forgot_password_email_sent")}
+
+ +
+ {i18next.t("sent_code_spam")} +
+
+
+ +
+ ); +}; + +export default ForgotPasswordSentPage; diff --git a/packages/authhero/src/components/Form.tsx b/packages/authhero/src/components/Form.tsx new file mode 100644 index 0000000..7043fe8 --- /dev/null +++ b/packages/authhero/src/components/Form.tsx @@ -0,0 +1,15 @@ +import { PropsWithChildren } from "hono/jsx"; + +type Props = { + className?: string; +}; + +const Form = ({ children, className }: PropsWithChildren) => { + return ( +
+ {children} +
+ ); +}; + +export default Form; diff --git a/packages/authhero/src/components/GoBack.tsx b/packages/authhero/src/components/GoBack.tsx new file mode 100644 index 0000000..908b076 --- /dev/null +++ b/packages/authhero/src/components/GoBack.tsx @@ -0,0 +1,16 @@ +import i18next from "i18next"; + +type Props = { + state: string; +}; + +export const GoBack = (props: Props) => { + return ( + + {i18next.t("go_back")} + + ); +}; diff --git a/packages/authhero/src/components/GoogleLogo.tsx b/packages/authhero/src/components/GoogleLogo.tsx new file mode 100644 index 0000000..fbebc77 --- /dev/null +++ b/packages/authhero/src/components/GoogleLogo.tsx @@ -0,0 +1,30 @@ +const Google = ({ ...props }) => { + return ( + + + + + + + ); +}; + +export default Google; diff --git a/packages/authhero/src/components/Icon.tsx b/packages/authhero/src/components/Icon.tsx new file mode 100644 index 0000000..6415b57 --- /dev/null +++ b/packages/authhero/src/components/Icon.tsx @@ -0,0 +1,26 @@ +import cn from "classnames"; + +type IconSizes = "small" | "medium" | "large"; + +type Props = { + name: string; + size?: IconSizes; + // still need to call prop className because class is a reserved keyword + className?: string; +}; + +const getTailwindSize = (size: IconSizes | undefined) => { + if (size === "small") return "text-base"; + if (size === "medium") return "text-2xl"; + if (size === "large") return "text-3xl"; + + return ""; +}; + +const Icon = ({ name, size, className = "" }: Props) => { + const tailwindSize = getTailwindSize(size); + + return ; +}; + +export default Icon; diff --git a/packages/authhero/src/components/InvalidSession.tsx b/packages/authhero/src/components/InvalidSession.tsx new file mode 100644 index 0000000..befab68 --- /dev/null +++ b/packages/authhero/src/components/InvalidSession.tsx @@ -0,0 +1,36 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "@authhero/adapter-interfaces"; +import i18next from "i18next"; + +type Props = { + redirectUrl?: string; + vendorSettings: VendorSettings; +}; + +const InvalidSessionPage: FC = (params) => { + const { redirectUrl, vendorSettings } = params; + + return ( + +
+ {i18next.t("invalid_session_body")} +
+
+ {redirectUrl && ( + + {i18next.t("go_back")} + + )} +
+
+ ); +}; + +export default InvalidSessionPage; diff --git a/packages/authhero/src/components/Layout.tsx b/packages/authhero/src/components/Layout.tsx new file mode 100644 index 0000000..80dd842 --- /dev/null +++ b/packages/authhero/src/components/Layout.tsx @@ -0,0 +1,137 @@ +import { VendorSettings } from "authhero"; +import AppLogo from "./AppLogo"; +import i18next from "i18next"; +import Footer from "./Footer"; +import Icon from "./Icon"; +import { html } from "hono/html"; +import { PropsWithChildren } from "hono/jsx"; + +type LayoutProps = { + title: string; + vendorSettings: VendorSettings; +}; + +const globalDocStyle = (vendorSettings: VendorSettings) => { + const { style } = vendorSettings; + // cannot render CSS directly in JSX but we can return a template string + return ` + body { + --primary-color: ${style.primaryColor}; + --primary-hover: ${style.primaryHoverColor}; + --text-on-primary: ${style.buttonTextColor}; + } + `; +}; + +const DEFAULT_BG = "https://assets.sesamy.com/images/login-bg.jpg"; + +const Layout = ({ + title, + children, + vendorSettings, +}: PropsWithChildren) => { + const inlineStyles = { + backgroundImage: `url(${ + vendorSettings?.loginBackgroundImage || DEFAULT_BG + })`, + }; + + return ( + + + {title} + + + + + + + + + + + + +
+
+
+
+
+ +
+
+ {children} +
+
+
+ +
+
+ + + +
+
+ {vendorSettings.supportUrl && ( + + {i18next.t("contact_support")} + + )} + |{" "} + {i18next.t("copyright_sesamy")} +
+
+
+
+
+ + {html` + + `} + + ); +}; + +export default Layout; diff --git a/packages/authhero/src/components/Message.tsx b/packages/authhero/src/components/Message.tsx new file mode 100644 index 0000000..bebe2e8 --- /dev/null +++ b/packages/authhero/src/components/Message.tsx @@ -0,0 +1,25 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import { GoBack } from "./GoBack"; + +type Props = { + message: string; + vendorSettings: VendorSettings; + pageTitle?: string; + state?: string; +}; + +const MessagePage: FC = (params) => { + const { message, vendorSettings, pageTitle, state } = params; + + return ( + + {pageTitle ?
{pageTitle}
: ""} +
{message}
+ {state ? : ""} +
+ ); +}; + +export default MessagePage; diff --git a/packages/authhero/src/components/PreSignUpConfirmationPage.tsx b/packages/authhero/src/components/PreSignUpConfirmationPage.tsx new file mode 100644 index 0000000..adc74c2 --- /dev/null +++ b/packages/authhero/src/components/PreSignUpConfirmationPage.tsx @@ -0,0 +1,51 @@ +import type { FC, JSXNode } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import i18next from "i18next"; +import { GoBack } from "./GoBack"; +import Icon from "./Icon"; +import Trans from "./Trans"; + +type Props = { + vendorSettings: VendorSettings; + email: string; + state: string; +}; + +const PreSignupComfirmationPage: FC = (params) => { + const { vendorSettings, email, state } = params; + + return ( + +
+ {i18next.t("email_verification_for_signup_sent_title")} +
+
+
+ + ) as unknown as JSXNode, + ]} + values={{ email }} + /> +
+
+ +
+ {/* translation string should just be sent_spam */} + {i18next.t("sent_code_spam")} +
+
+
+ +
+ ); +}; + +export default PreSignupComfirmationPage; diff --git a/packages/authhero/src/components/PreSignUpPage.tsx b/packages/authhero/src/components/PreSignUpPage.tsx new file mode 100644 index 0000000..e97f602 --- /dev/null +++ b/packages/authhero/src/components/PreSignUpPage.tsx @@ -0,0 +1,52 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import i18next from "i18next"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; +import { GoBack } from "./GoBack"; + +type Props = { + state: string; + vendorSettings: VendorSettings; + email?: string; +}; + +const PreSignupPage: FC = (params) => { + const { vendorSettings, email, state } = params; + + return ( + +
+ {i18next.t("create_password_account_title")} +
+
+ {i18next.t("enter_email_for_verification_description")} +
+
+
+ + +
+ {i18next.t("send")} +
+
+
+
+ +
+ ); +}; + +export default PreSignupPage; diff --git a/packages/authhero/src/components/ResetPasswordPage.tsx b/packages/authhero/src/components/ResetPasswordPage.tsx new file mode 100644 index 0000000..76cfbc4 --- /dev/null +++ b/packages/authhero/src/components/ResetPasswordPage.tsx @@ -0,0 +1,53 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import i18next from "i18next"; +import ErrorMessage from "./ErrorMessage"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; + +type ResetPasswordPageProps = { + error?: string; + vendorSettings: VendorSettings; + email: string; +}; + +const ResetPasswordPage: FC = (params) => { + const { error, vendorSettings, email } = params; + + return ( + +
+ {i18next.t("reset_password_title")} +
+
+ {`${i18next.t("reset_password_description")} ${email}`} +
+
+
+ + + {error && {error}} + + {i18next.t("reset_password_cta")} + +
+
+
+ ); +}; + +export default ResetPasswordPage; diff --git a/packages/authhero/src/components/SignUpPage.tsx b/packages/authhero/src/components/SignUpPage.tsx new file mode 100644 index 0000000..829faec --- /dev/null +++ b/packages/authhero/src/components/SignUpPage.tsx @@ -0,0 +1,67 @@ +import type { FC } from "hono/jsx"; +import Layout from "./Layout"; +import { VendorSettings } from "authhero"; +import ErrorMessage from "./ErrorMessage"; +import i18next from "i18next"; +import DisabledSubmitButton from "./DisabledSubmitButton"; +import Form from "./Form"; +import { GoBack } from "./GoBack"; + +type Props = { + state: string; + error?: string; + vendorSettings: VendorSettings; + email?: string; + code?: string; +}; + +const SignupPage: FC = (params) => { + const { state, error, vendorSettings, email, code } = params; + + return ( + +
+ {i18next.t("create_account_title")} +
+
+ {i18next.t("create_account_description")} +
+
+
+ + + + + {error && {error}} + + {i18next.t("continue")} + +
+ +
+
+ ); +}; + +export default SignupPage; diff --git a/packages/authhero/src/components/SocialButton.tsx b/packages/authhero/src/components/SocialButton.tsx new file mode 100644 index 0000000..e37f587 --- /dev/null +++ b/packages/authhero/src/components/SocialButton.tsx @@ -0,0 +1,71 @@ +import cn from "classnames"; +import Button from "./Button"; +import { Login } from "authhero"; + +type Props = { + connection: "google-oauth2" | "apple" | "facebook" | "vipps"; + // TODO - what is the correct type here in hono/jsx? OR use a children prop + icon: any; + text: string; + canResize?: boolean; + session: Login; +}; + +const SocialButton = ({ + connection, + text, + icon = null, + canResize = false, + session, +}: Props) => { + const queryString = new URLSearchParams({ + client_id: session.authParams.client_id, + connection, + }); + if (session.authParams.response_type) { + queryString.set("response_type", session.authParams.response_type); + } + if (session.authParams.redirect_uri) { + queryString.set("redirect_uri", session.authParams.redirect_uri); + } + if (session.authParams.scope) { + queryString.set("scope", session.authParams.scope); + } + if (session.authParams.nonce) { + queryString.set("nonce", session.authParams.nonce); + } + if (session.authParams.response_type) { + queryString.set("response_type", session.authParams.response_type); + } + if (session.authParams.state) { + queryString.set("state", session.login_id); + } + const href = `/authorize?${queryString.toString()}`; + + return ( + + ); +}; + +export default SocialButton; diff --git a/packages/authhero/src/components/Spinner.tsx b/packages/authhero/src/components/Spinner.tsx new file mode 100644 index 0000000..a960807 --- /dev/null +++ b/packages/authhero/src/components/Spinner.tsx @@ -0,0 +1,38 @@ +import cn from "classnames"; +import Icon from "./Icon"; + +type SpinnerSizes = "small" | "medium" | "large"; + +type Props = { + size?: SpinnerSizes; +}; + +const getTailwindSize = (size: SpinnerSizes | undefined) => { + if (size === "small") return "text-base"; + if (size === "medium") return "text-2xl"; + if (size === "large") return "text-3xl"; + + return ""; +}; + +const Spinner = ({ size }: Props) => { + const tailwindSize = getTailwindSize(size); + return ( +
+ + +
+ ); +}; + +export default Spinner; diff --git a/packages/authhero/src/components/Trans.tsx b/packages/authhero/src/components/Trans.tsx new file mode 100644 index 0000000..e3fbe02 --- /dev/null +++ b/packages/authhero/src/components/Trans.tsx @@ -0,0 +1,40 @@ +// Trans.tsx +import { FC, JSXNode, cloneElement } from "hono/jsx"; +import i18next from "i18next"; + +interface TransProps { + i18nKey: string; + values: Record; + components: JSXNode[]; +} + +const Trans: FC = (params) => { + const { i18nKey, values, components } = params; + + const translation = i18next.t(i18nKey, values); + const regex = /<(\d+)>(.*?)<\/\d+>/g; + + const result: (string | JSXNode)[] = []; + let lastIndex = 0; + let match: RegExpExecArray | null; + + while ((match = regex.exec(translation)) !== null) { + const [, index, content] = match; + const precedingText = translation.substring(lastIndex, match.index); + if (precedingText) { + result.push(precedingText); + } + const componentIndex = parseInt(index!, 10); + // @ts-ignore + result.push(cloneElement(components[componentIndex], {}, content)); + lastIndex = regex.lastIndex; + } + + if (lastIndex < translation.length) { + result.push(translation.substring(lastIndex)); + } + + return <>{result}; +}; + +export default Trans; diff --git a/packages/authhero/src/components/UnverifiedEmailPage.tsx b/packages/authhero/src/components/UnverifiedEmailPage.tsx new file mode 100644 index 0000000..2dd4321 --- /dev/null +++ b/packages/authhero/src/components/UnverifiedEmailPage.tsx @@ -0,0 +1,39 @@ +import Layout from "./Layout"; +import Icon from "./Icon"; +import i18next from "i18next"; +import type { FC } from "hono/jsx"; +import { VendorSettings } from "authhero"; +import { GoBack } from "./GoBack"; + +type Props = { + vendorSettings: VendorSettings; + state: string; +}; + +const UnverifiedEmailPage: FC = (params) => { + const { vendorSettings, state } = params; + + return ( + +
+

+ {i18next.t("unverified_email")} +

+
+ +
+ {/* translation string should just be sent_spam */} + {i18next.t("sent_code_spam")} +
+
+ +
+ +
+ ); +}; + +export default UnverifiedEmailPage; diff --git a/packages/authhero/src/components/UserNotFoundPage.tsx b/packages/authhero/src/components/UserNotFoundPage.tsx new file mode 100644 index 0000000..f090748 --- /dev/null +++ b/packages/authhero/src/components/UserNotFoundPage.tsx @@ -0,0 +1,35 @@ +import Button from "./Button"; +import Layout from "./Layout"; +import i18next from "i18next"; +import type { FC } from "hono/jsx"; +import { AuthParams, VendorSettings } from "authhero"; + +type Props = { + error?: string; + vendorSettings: VendorSettings; + authParams: AuthParams; +}; + +const UserNotFound: FC = (params) => { + const { vendorSettings, authParams } = params; + + const linkParams = new URLSearchParams({ + ...authParams, + }); + const restartFlowLink = `/authorize?${linkParams}`; + + return ( + +
+

+ {i18next.t("user_not_found_body")} +

+ +
+
+ ); +}; + +export default UserNotFound; diff --git a/packages/authhero/src/components/VippsLogo.tsx b/packages/authhero/src/components/VippsLogo.tsx new file mode 100644 index 0000000..77ae5d9 --- /dev/null +++ b/packages/authhero/src/components/VippsLogo.tsx @@ -0,0 +1,29 @@ +const VippsLogo = ({ ...props }) => ( + + + + +); + +export default VippsLogo; diff --git a/packages/authhero/src/index.ts b/packages/authhero/src/index.ts index 46fbb55..1b9cac8 100644 --- a/packages/authhero/src/index.ts +++ b/packages/authhero/src/index.ts @@ -2,8 +2,9 @@ import { OpenAPIHono } from "@hono/zod-openapi"; import { Context } from "hono"; import i18next from "i18next"; import { Bindings, Variables } from "./types"; -import createManagementApi from "./management-app"; -import createOauthApi from "./auth-app"; +import createManagementApi from "./routes/management-api"; +import createOauthApi from "./routes/auth-api"; +import createUniversalLogin from "./routes/universal-login"; import { AuthHeroConfig } from "./types/AuthHeroConfig"; import { addDataHooks } from "./hooks"; import { createX509Certificate } from "./helpers/encryption"; @@ -46,10 +47,14 @@ export function init(config: AuthHeroConfig) { const oauthApp = createOauthApi(); app.route("/", oauthApp); + const universalApp = createUniversalLogin(); + app.route("/u", universalApp); + return { app, managementApp, oauthApp, + universalApp, createX509Certificate, }; } diff --git a/packages/authhero/src/routes/auth-api/index.ts b/packages/authhero/src/routes/auth-api/index.ts index 666faca..c40dded 100644 --- a/packages/authhero/src/routes/auth-api/index.ts +++ b/packages/authhero/src/routes/auth-api/index.ts @@ -1,8 +1,51 @@ -export * from "./well-known"; -export * from "./token"; -export * from "./logout"; -export * from "./userinfo"; -export * from "./dbconnections"; -export * from "./passwordless"; -export * from "./authenticate"; -export * from "./authorize"; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { Bindings, Variables } from "../../types"; +import { registerComponent } from "../../middlewares/register-component"; +import { DataAdapters } from "@authhero/adapter-interfaces"; +import { createAuthMiddleware } from "../../middlewares/authentication"; + +import { callbackRoutes } from "./callback"; +import { logoutRoutes } from "./logout"; +import { userinfoRoutes } from "./userinfo"; +import { wellKnownRoutes } from "./well-known"; +import { tokenRoutes } from "./token"; +import { dbConnectionRoutes } from "./dbconnections"; +import { passwordlessRoutes } from "./passwordless"; +import { authenticateRoutes } from "./authenticate"; +import { authorizeRoutes } from "./authorize"; + +export interface CreateAuthParams { + dataAdapter: DataAdapters; +} + +export default function create() { + const app = new OpenAPIHono<{ + Bindings: Bindings; + Variables: Variables; + }>(); + + app.use(createAuthMiddleware(app)); + + const oauthApp = app + .route("/v2/logout", logoutRoutes) + .route("/userinfo", userinfoRoutes) + .route("/.well-known", wellKnownRoutes) + .route("/oauth/token", tokenRoutes) + .route("/dbconnections", dbConnectionRoutes) + .route("/passwordless", passwordlessRoutes) + .route("/co/authenticate", authenticateRoutes) + .route("/authorize", authorizeRoutes) + .route("/callback", callbackRoutes); + + oauthApp.doc("/spec", { + openapi: "3.0.0", + info: { + version: "1.0.0", + title: "Oauth API", + }, + }); + + registerComponent(oauthApp); + + return oauthApp; +} diff --git a/packages/authhero/src/management-app.ts b/packages/authhero/src/routes/management-api/index.ts similarity index 51% rename from packages/authhero/src/management-app.ts rename to packages/authhero/src/routes/management-api/index.ts index 9aa7aa9..aaaf9ab 100644 --- a/packages/authhero/src/management-app.ts +++ b/packages/authhero/src/routes/management-api/index.ts @@ -1,20 +1,19 @@ import { OpenAPIHono } from "@hono/zod-openapi"; -import { Bindings, Variables } from "./types"; -import { brandingRoutes } from "./routes/management-api/branding"; -// import { domainRoutes } from "./routes/management-api/domains"; -import { userRoutes } from "./routes/management-api/users"; -import { keyRoutes } from "./routes/management-api/keys"; -import { usersByEmailRoutes } from "./routes/management-api/users-by-email"; -import { clientRoutes } from "./routes/management-api/clients"; -import { tenantRoutes } from "./routes/management-api/tenants"; -import { logRoutes } from "./routes/management-api/logs"; -import { hooksRoutes } from "./routes/management-api/hooks"; -import { connectionRoutes } from "./routes/management-api/connections"; -import { promptsRoutes } from "./routes/management-api/prompts"; -import { registerComponent } from "./middlewares/register-component"; +import { Bindings, Variables } from "../../types"; +import { brandingRoutes } from "./branding"; +import { userRoutes } from "./users"; +import { keyRoutes } from "./keys"; +import { usersByEmailRoutes } from "./users-by-email"; +import { clientRoutes } from "./clients"; +import { tenantRoutes } from "./tenants"; +import { logRoutes } from "./logs"; +import { hooksRoutes } from "./hooks"; +import { connectionRoutes } from "./connections"; +import { promptsRoutes } from "./prompts"; +import { registerComponent } from "../../middlewares/register-component"; import { DataAdapters } from "@authhero/adapter-interfaces"; -import { createAuthMiddleware } from "./middlewares/authentication"; -import { emailProviderRoutes } from "./routes/management-api/emails"; +import { createAuthMiddleware } from "../../middlewares/authentication"; +import { emailProviderRoutes } from "./emails"; export interface CreateAuthParams { dataAdapter: DataAdapters; diff --git a/packages/authhero/src/routes/universal-login/common.tsx b/packages/authhero/src/routes/universal-login/common.tsx new file mode 100644 index 0000000..0795b24 --- /dev/null +++ b/packages/authhero/src/routes/universal-login/common.tsx @@ -0,0 +1,198 @@ +import { Context } from "hono"; +import { HTTPException } from "hono/http-exception"; +import { getClientWithDefaults } from "../../helpers/client"; +import i18next from "i18next"; +import { + Client, + Login, + LogTypes, + User, + VendorSettings, + vendorSettingsSchema, +} from "@authhero/adapter-interfaces"; +import { getPrimaryUserByEmail } from "../../helpers/users"; +import { Bindings, Variables } from "../../types"; +import MessagePage from "../../components/Message"; +import { createAuthResponse } from "../../authentication-flows/common"; + +// there is no Sesamy vendor settings... we have this on login2 as a fallback and I think there's +// some interaction with "dark mode" +// But I don't want to have a Sesamy vendor on auth2 +export const SESAMY_VENDOR_SETTINGS: VendorSettings = { + name: "sesamy", + logoUrl: `https://assets.sesamy.com/static/images/email/sesamy-logo.png`, + style: { + primaryColor: "#7D68F4", + buttonTextColor: "#FFFFFF", + primaryHoverColor: "#A091F2", + }, + loginBackgroundImage: "", + checkoutHideSocial: false, + supportEmail: "support@sesamy.com", + supportUrl: "https://support.sesamy.com", + siteUrl: "https://sesamy.com", + termsAndConditionsUrl: "https://store.sesamy.com/pages/terms-of-service", + manageSubscriptionsUrl: "https://account.sesamy.com/manage-subscriptions", +}; + +export async function fetchVendorSettings( + env: Bindings, + client_id?: string, + vendor_id?: string, +) { + if (!vendor_id && !client_id) { + return SESAMY_VENDOR_SETTINGS; + } + + const vendorId = vendor_id || client_id; + + try { + const vendorSettingsRes = await fetch( + `${env.API_URL}/profile/vendors/${vendorId}/style`, + ); + + if (!vendorSettingsRes.ok) { + throw new Error("Failed to fetch vendor settings"); + } + + const vendorSettingsRaw = await vendorSettingsRes.json(); + + const vendorSettings = vendorSettingsSchema.parse(vendorSettingsRaw); + + return vendorSettings; + } catch (e) { + console.error(e); + return SESAMY_VENDOR_SETTINGS; + } +} + +export async function initJSXRoute(ctx: Context, state: string) { + const { env } = ctx; + const login = await env.data.logins.get(ctx.var.tenant_id || "", state); + if (!login) { + throw new HTTPException(400, { message: "Session not found" }); + } + ctx.set("login", login); + + const client = await getClientWithDefaults(env, login.authParams.client_id); + ctx.set("client_id", client.id); + ctx.set("tenant_id", client.tenant.id); + + const tenant = await env.data.tenants.get(client.tenant.id); + if (!tenant) { + throw new HTTPException(400, { message: "Tenant not found" }); + } + + const vendorSettings = await fetchVendorSettings( + env, + client.id, + login.authParams.vendor_id, + ); + + const loginSessionLanguage = login.authParams.ui_locales + ?.split(" ") + .map((locale) => locale.split("-")[0]) + .find((language) => { + if (Array.isArray(i18next.options.supportedLngs)) { + return i18next.options.supportedLngs.includes(language); + } + }); + + await i18next.changeLanguage(loginSessionLanguage || tenant.language || "sv"); + + return { + vendorSettings: { + ...vendorSettings, + // HACK: Change the terms and conditions for fokus app + termsAndConditionsUrl: + client.id === "fokus-app" + ? "https://www.fokus.se/kopvillkor-app/" + : vendorSettings.termsAndConditionsUrl, + }, + client, + tenant, + session: login, + }; +} + +export async function handleLogin( + ctx: Context<{ + Bindings: Bindings; + Variables: Variables; + }>, + user: User, + session: Login, + client: Client, +) { + if (session.authParams.redirect_uri) { + ctx.set("username", user.email); + ctx.set("connection", user.connection); + ctx.set("user_id", user.user_id); + + return createAuthResponse(ctx, { + client, + authParams: session.authParams, + user, + }); + } + + const vendorSettings = await fetchVendorSettings( + ctx.env, + client.id, + session.authParams.vendor_id, + ); + + return ctx.html( + , + ); +} + +export async function usePasswordLogin( + ctx: Context, + client: Client, + username: string, + login_selection?: "password" | "code", +) { + if (login_selection !== undefined) { + return login_selection === "password"; + } + + // Get primary user for email + const user = await getPrimaryUserByEmail({ + userAdapter: ctx.env.data.users, + tenant_id: client.tenant.id, + email: username, + }); + + if (user) { + // Get last login + const lastLogins = await ctx.env.data.logs.list(client.tenant.id, { + page: 0, + per_page: 10, + include_totals: false, + sort: { sort_by: "date", sort_order: "desc" }, + q: `type:${LogTypes.SUCCESS_LOGIN} user_id:${user.user_id}`, + }); + + const [lastLogin] = lastLogins.logs.filter( + (log) => + log.strategy && + ["Username-Password-Authentication", "passwordless", "email"].includes( + log.strategy, + ), + ); + + if (lastLogin) { + return lastLogin.strategy === "Username-Password-Authentication"; + } + } + + const promptSettings = await ctx.env.data.promptSettings.get( + client.tenant.id, + ); + return promptSettings.password_first; +} diff --git a/packages/authhero/src/routes/universal-login/enter-email.tsx b/packages/authhero/src/routes/universal-login/enter-email.tsx new file mode 100644 index 0000000..a27c638 --- /dev/null +++ b/packages/authhero/src/routes/universal-login/enter-email.tsx @@ -0,0 +1,216 @@ +import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"; +import { Bindings, Variables } from "../../types"; +import { initJSXRoute, usePasswordLogin } from "./common"; +import EnterEmailPage from "../../components/EnterEmailPage"; +import { getPrimaryUserByEmail } from "../../helpers/users"; +import { preUserSignupHook } from "../../hooks"; +import { createLogMessage } from "../../utils/create-log-message"; +import { LogTypes } from "@authhero/adapter-interfaces"; +import i18next from "i18next"; +import generateOTP from "../../utils/otp"; +import { waitUntil } from "../../helpers/wait-until"; +import { sendCode, sendLink } from "../../emails"; +import { OTP_EXPIRATION_TIME } from "src/constants"; + +type Auth0Client = { + name: string; + version: string; +}; + +const APP_CLIENT_IDS = ["Auth0.swift"]; + +export type SendType = "link" | "code"; + +export function getSendParamFromAuth0ClientHeader( + auth0ClientHeader?: string, +): SendType { + if (!auth0ClientHeader) return "link"; + + const decodedAuth0Client = atob(auth0ClientHeader); + + const auth0Client = JSON.parse(decodedAuth0Client) as Auth0Client; + + const isAppClient = APP_CLIENT_IDS.includes(auth0Client.name); + + return isAppClient ? "code" : "link"; +} + +export const emailRoutes = new OpenAPIHono<{ + Bindings: Bindings; + Variables: Variables; +}>() + // -------------------------------- + // GET /u/enter-email + // -------------------------------- + .openapi( + createRoute({ + tags: ["login"], + method: "get", + path: "/enter-email", + request: { + query: z.object({ + state: z.string().openapi({ + description: "The state parameter from the authorization request", + }), + impersonation: z.string().optional(), + }), + }, + responses: { + 200: { + description: "Response", + }, + }, + }), + async (ctx) => { + const { state, impersonation } = ctx.req.valid("query"); + + const { vendorSettings, session, client } = await initJSXRoute( + ctx, + state, + ); + + return ctx.html( + , + ); + }, + ) + // -------------------------------- + // POST /u/enter-email + // -------------------------------- + .openapi( + createRoute({ + tags: ["login"], + method: "post", + path: "/enter-email", + request: { + query: z.object({ + state: z.string().openapi({ + description: "The state parameter from the authorization request", + }), + }), + body: { + content: { + "application/x-www-form-urlencoded": { + schema: z.object({ + username: z.string().transform((u) => u.toLowerCase()), + act_as: z + .string() + .transform((u) => u.toLowerCase()) + .optional(), + login_selection: z.enum(["code", "password"]).optional(), + }), + }, + }, + }, + }, + responses: { + 200: { + description: "Response", + }, + }, + }), + async (ctx) => { + const { env } = ctx; + const { state } = ctx.req.valid("query"); + const params = ctx.req.valid("form"); + ctx.set("body", params); + ctx.set("username", params.username); + + const { client, session, vendorSettings } = await initJSXRoute( + ctx, + state, + ); + ctx.set("client_id", client.id); + + const username = params.username; + + const user = await getPrimaryUserByEmail({ + userAdapter: env.data.users, + tenant_id: client.tenant.id, + email: username, + }); + if (user) { + ctx.set("user_id", user.user_id); + } + + if (!user) { + try { + await preUserSignupHook(ctx, client, ctx.env.data, params.username); + } catch { + const log = createLogMessage(ctx, { + type: LogTypes.FAILED_SIGNUP, + description: "Public signup is disabled", + }); + + await ctx.env.data.logs.create(client.tenant.id, log); + + return ctx.html( + , + 400, + ); + } + } + + // Add the username to the state + session.authParams.username = params.username; + session.authParams.act_as = params.act_as; + await env.data.logins.update(client.tenant.id, session.login_id, session); + + if ( + await usePasswordLogin( + ctx, + client, + params.username, + params.login_selection, + ) + ) { + return ctx.redirect(`/u/enter-password?state=${state}`); + } + + let code_id = generateOTP(); + let existingCode = await env.data.codes.get( + client.tenant.id, + code_id, + "otp", + ); + + // This is a slighly hacky way to ensure we don't generate a code that already exists + while (existingCode) { + code_id = generateOTP(); + existingCode = await env.data.codes.get( + client.tenant.id, + code_id, + "otp", + ); + } + + const createdCode = await ctx.env.data.codes.create(client.tenant.id, { + code_id, + code_type: "otp", + login_id: session.login_id, + expires_at: new Date(Date.now() + OTP_EXPIRATION_TIME).toISOString(), + }); + + const sendType = getSendParamFromAuth0ClientHeader(session.auth0Client); + + if (sendType === "link" && !params.username.includes("online.no")) { + waitUntil(ctx, sendLink(ctx, params.username, createdCode.code_id)); + } else { + waitUntil(ctx, sendCode(ctx, params.username, createdCode.code_id)); + } + + return ctx.redirect(`/u/enter-code?state=${state}`); + }, + ); diff --git a/packages/authhero/src/routes/universal-login/index.ts b/packages/authhero/src/routes/universal-login/index.ts new file mode 100644 index 0000000..14f4ddc --- /dev/null +++ b/packages/authhero/src/routes/universal-login/index.ts @@ -0,0 +1,26 @@ +import { OpenAPIHono } from "@hono/zod-openapi"; +import { Bindings, Variables } from "../../types"; +import { DataAdapters } from "@authhero/adapter-interfaces"; + +export interface CreateAuthParams { + dataAdapter: DataAdapters; +} + +export default function create() { + const app = new OpenAPIHono<{ + Bindings: Bindings; + Variables: Variables; + }>(); + + const universalApp = app; + + universalApp.doc("/u/spec", { + openapi: "3.0.0", + info: { + version: "1.0.0", + title: "Universal login", + }, + }); + + return universalApp; +} diff --git a/packages/authhero/src/types/Bindings.ts b/packages/authhero/src/types/Bindings.ts index 5bc2f64..9ac67d1 100644 --- a/packages/authhero/src/types/Bindings.ts +++ b/packages/authhero/src/types/Bindings.ts @@ -32,4 +32,7 @@ export type Bindings = { JWKS_CACHE_TIMEOUT_IN_SECONDS: number; // This is used as CN in the certificate ORGANIZATION_NAME: string; + + // TODO: Move once the vendorsettings are updated + API_URL: string; }; diff --git a/packages/authhero/test/helpers/test-server.ts b/packages/authhero/test/helpers/test-server.ts index e1ee006..4b7d4bb 100644 --- a/packages/authhero/test/helpers/test-server.ts +++ b/packages/authhero/test/helpers/test-server.ts @@ -8,7 +8,6 @@ import createAdapters, { import * as x509 from "@peculiar/x509"; import { init } from "../../src"; import { getCertificate } from "./token"; -import { Tenant } from "@authhero/kysely-adapter"; import { Bindings } from "../../src/types"; import { MockEmailService } from "./mock-email-service"; import { mockStrategy } from "./mock-strategy"; @@ -95,6 +94,7 @@ export async function getTestServer(args: getEnvParams = {}) { JWKS_URL: "http://localhost:3000/.well-known/jwks.json", AUTH_URL: "http://localhost:3000", ISSUER: "http://localhost:3000/", + API_URL: "http://localhost:3000", ENVIRONMENT: "test", JWKS_CACHE_TIMEOUT_IN_SECONDS: 3600, ORGANIZATION_NAME: "Test Organization", diff --git a/packages/authhero/tsconfig.json b/packages/authhero/tsconfig.json index 13a8bd3..8c064e5 100644 --- a/packages/authhero/tsconfig.json +++ b/packages/authhero/tsconfig.json @@ -4,7 +4,9 @@ // "noEmit": true, "baseUrl": ".", "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" }, "include": [ "src", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b5b737..75031cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,26 +61,29 @@ importers: apps/demo: dependencies: '@authhero/kysely-adapter': - specifier: ^0.26.1 - version: 0.26.1(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(kysely-bun-sqlite@0.3.2(kysely@0.27.4))(kysely-planetscale@1.5.0(@planetscale/database@1.18.0)(kysely@0.27.4)) + specifier: ^0.28.1 + version: 0.28.1(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(kysely-bun-sqlite@0.3.2(kysely@0.27.4))(kysely-planetscale@1.5.0(@planetscale/database@1.18.0)(kysely@0.27.4)) '@hono/swagger-ui': specifier: ^0.5.0 - version: 0.5.0(hono@4.6.13) + version: 0.5.0(hono@4.6.15) '@hono/zod-openapi': specifier: ^0.18.3 - version: 0.18.3(hono@4.6.13)(zod@3.23.8) + version: 0.18.3(hono@4.6.15)(zod@3.23.8) '@peculiar/x509': specifier: ^1.12.3 version: 1.12.3 authhero: - specifier: ^0.35.0 - version: 0.35.0(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(typescript@5.6.3) + specifier: ^0.36.2 + version: 0.36.2(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(typescript@5.6.3) hono: - specifier: ^4.6.13 - version: 4.6.13 + specifier: ^4.6.15 + version: 4.6.15 hono-openapi-middlewares: specifier: ^1.0.11 - version: 1.0.11(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(zod@3.23.8) + version: 1.0.11(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(zod@3.23.8) + kysely: + specifier: ^0.27.4 + version: 0.27.4 kysely-bun-sqlite: specifier: ^0.3.2 version: 0.3.2(kysely@0.27.4) @@ -187,7 +190,7 @@ importers: devDependencies: '@hono/zod-openapi': specifier: ^0.18.0 - version: 0.18.0(hono@4.6.13)(zod@3.23.8) + version: 0.18.0(hono@4.6.15)(zod@3.23.8) '@rollup/plugin-commonjs': specifier: ^28.0.1 version: 28.0.1(rollup@4.24.0) @@ -224,6 +227,9 @@ importers: bcryptjs: specifier: ^2.4.3 version: 2.4.3 + classnames: + specifier: ^2.5.1 + version: 2.5.1 i18next: specifier: ^24.2.0 version: 24.2.0(typescript@5.6.3) @@ -381,13 +387,13 @@ packages: '@auth0/auth0-spa-js@2.1.3': resolution: {integrity: sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==} - '@authhero/adapter-interfaces@0.35.0': - resolution: {integrity: sha512-d63q++13yiurLtYziOrr4e4NAND3MBP4DDQLVKND2u7dt4xLuPIYmrKfv11WqlNLz28qP8hJJq9miLYpszK48A==} + '@authhero/adapter-interfaces@0.36.0': + resolution: {integrity: sha512-QTZlp8J5KJlN91DoL5FSjkU8VhxPMrU+4Zeoomt30uF3eqy72gE1pcr6/E6TJF+Dih0533NzoOD53RSY8NtFZg==} peerDependencies: '@hono/zod-openapi': ^0.16.4 - '@authhero/kysely-adapter@0.26.1': - resolution: {integrity: sha512-9oi3V7nfnu6UZts9rn7e8vNOMN2GVMQazVr+qKehXcyLJfOxXLl6zAJ2TWaDAArdZKZow06WLFS6U3+5oml7MA==} + '@authhero/kysely-adapter@0.28.1': + resolution: {integrity: sha512-jMZ4KR+qwAM28w+22z4bjtMojIAa6OdyNvkga4mWsEbkRpvmJRcdjCkAbGYvMdZhtVvdUmgvTq/y+ke5MjTp4Q==} peerDependencies: '@hono/zod-openapi': ^0.16.4 hono: ^4.6.8 @@ -1976,8 +1982,8 @@ packages: resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==} engines: {node: '>=4'} - authhero@0.35.0: - resolution: {integrity: sha512-YPyJ0M9yrZPiTTo9BP7pP3r+NG+HSTvyd1eaAmGocuJfW9Sn2B07TnFcBkn/41wKi88exiHSKsDMoUfVHvct1Q==} + authhero@0.36.2: + resolution: {integrity: sha512-r2SH8Uc4WzSjaxMssleOiDSwn78P9cxW7oeKrtaGnJjKE+0CSfVagdOdTwFG2w5Jou+ZHosbiMnT9aRONPJv1A==} peerDependencies: '@hono/zod-openapi': ^0.18.0 hono: ^4.6.11 @@ -2096,6 +2102,9 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -2753,8 +2762,8 @@ packages: resolution: {integrity: sha512-f0LwJQFKdUUrCUAVowxSvNCjyzI7ZLt8XWYU/EApyeq5FfOvHFarBaE5rjU9HTNFk4RI0FkdB2edb3p/7xZjzQ==} engines: {node: '>=16.9.0'} - hono@4.6.13: - resolution: {integrity: sha512-haV0gaMdSjy9URCRN9hxBPlqHa7fMm/T72kAImIxvw4eQLbNz1rgjN4hHElLJSieDiNuiIAXC//cC6YGz2KCbg==} + hono@4.6.15: + resolution: {integrity: sha512-OiQwvAOAaI2JrABBH69z5rsctHDzFzIKJge0nYXgtzGJ0KftwLWcBXm1upJC23/omNRtnqM0gjRMbtXshPdqhQ==} engines: {node: '>=16.9.0'} hotscript@1.0.13: @@ -4594,16 +4603,16 @@ snapshots: '@auth0/auth0-spa-js@2.1.3': {} - '@authhero/adapter-interfaces@0.35.0(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))': + '@authhero/adapter-interfaces@0.36.0(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))': dependencies: - '@hono/zod-openapi': 0.18.3(hono@4.6.13)(zod@3.23.8) + '@hono/zod-openapi': 0.18.3(hono@4.6.15)(zod@3.23.8) nanoid: 5.0.8 - '@authhero/kysely-adapter@0.26.1(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(kysely-bun-sqlite@0.3.2(kysely@0.27.4))(kysely-planetscale@1.5.0(@planetscale/database@1.18.0)(kysely@0.27.4))': + '@authhero/kysely-adapter@0.28.1(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(kysely-bun-sqlite@0.3.2(kysely@0.27.4))(kysely-planetscale@1.5.0(@planetscale/database@1.18.0)(kysely@0.27.4))': dependencies: - '@authhero/adapter-interfaces': 0.35.0(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8)) - '@hono/zod-openapi': 0.18.3(hono@4.6.13)(zod@3.23.8) - hono: 4.6.13 + '@authhero/adapter-interfaces': 0.36.0(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8)) + '@hono/zod-openapi': 0.18.3(hono@4.6.15)(zod@3.23.8) + hono: 4.6.15 kysely: 0.27.4 kysely-bun-sqlite: 0.3.2(kysely@0.27.4) kysely-planetscale: 1.5.0(@planetscale/database@1.18.0)(kysely@0.27.4) @@ -5151,9 +5160,9 @@ snapshots: '@floating-ui/utils@0.2.3': {} - '@hono/swagger-ui@0.5.0(hono@4.6.13)': + '@hono/swagger-ui@0.5.0(hono@4.6.15)': dependencies: - hono: 4.6.13 + hono: 4.6.15 '@hono/zod-openapi@0.18.0(hono@4.6.11)(zod@3.23.8)': dependencies: @@ -5162,18 +5171,18 @@ snapshots: hono: 4.6.11 zod: 3.23.8 - '@hono/zod-openapi@0.18.0(hono@4.6.13)(zod@3.23.8)': + '@hono/zod-openapi@0.18.0(hono@4.6.15)(zod@3.23.8)': dependencies: '@asteasolutions/zod-to-openapi': 7.1.1(zod@3.23.8) - '@hono/zod-validator': 0.4.1(hono@4.6.13)(zod@3.23.8) - hono: 4.6.13 + '@hono/zod-validator': 0.4.1(hono@4.6.15)(zod@3.23.8) + hono: 4.6.15 zod: 3.23.8 - '@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8)': + '@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8)': dependencies: '@asteasolutions/zod-to-openapi': 7.1.1(zod@3.23.8) - '@hono/zod-validator': 0.4.1(hono@4.6.13)(zod@3.23.8) - hono: 4.6.13 + '@hono/zod-validator': 0.4.1(hono@4.6.15)(zod@3.23.8) + hono: 4.6.15 zod: 3.23.8 '@hono/zod-validator@0.4.1(hono@4.6.11)(zod@3.23.8)': @@ -5181,9 +5190,9 @@ snapshots: hono: 4.6.11 zod: 3.23.8 - '@hono/zod-validator@0.4.1(hono@4.6.13)(zod@3.23.8)': + '@hono/zod-validator@0.4.1(hono@4.6.15)(zod@3.23.8)': dependencies: - hono: 4.6.13 + hono: 4.6.15 zod: 3.23.8 '@humanwhocodes/module-importer@1.0.1': {} @@ -6445,15 +6454,15 @@ snapshots: attr-accept@2.2.2: {} - authhero@0.35.0(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(typescript@5.6.3): + authhero@0.36.2(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(typescript@5.6.3): dependencies: - '@authhero/adapter-interfaces': 0.35.0(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8)) - '@hono/zod-openapi': 0.18.3(hono@4.6.13)(zod@3.23.8) + '@authhero/adapter-interfaces': 0.36.0(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8)) + '@hono/zod-openapi': 0.18.3(hono@4.6.15)(zod@3.23.8) '@peculiar/x509': 1.12.3 arctic: 2.3.3 bcrypt: 5.1.1 bcryptjs: 2.4.3 - hono: 4.6.13 + hono: 4.6.15 i18next: 24.2.0(typescript@5.6.3) nanoid: 5.0.8 oslo: 1.2.1 @@ -6599,6 +6608,8 @@ snapshots: ci-info@3.9.0: {} + classnames@2.5.1: {} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -7385,17 +7396,17 @@ snapshots: dependencies: react-is: 16.13.1 - hono-openapi-middlewares@1.0.11(@hono/zod-openapi@0.18.3(hono@4.6.13)(zod@3.23.8))(hono@4.6.13)(zod@3.23.8): + hono-openapi-middlewares@1.0.11(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.23.8))(hono@4.6.15)(zod@3.23.8): dependencies: - '@hono/zod-openapi': 0.18.3(hono@4.6.13)(zod@3.23.8) - hono: 4.6.13 + '@hono/zod-openapi': 0.18.3(hono@4.6.15)(zod@3.23.8) + hono: 4.6.15 zod: 3.23.8 hono@4.4.10: {} hono@4.6.11: {} - hono@4.6.13: {} + hono@4.6.15: {} hotscript@1.0.13: {}