@henosia/app-next 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +183 -0
- package/README.md +173 -0
- package/dist/api/auth.d.mts +6 -0
- package/dist/api/auth.mjs +18 -0
- package/dist/api/platform.d.mts +19 -0
- package/dist/api/platform.mjs +48 -0
- package/dist/auth/client.d.mts +25 -0
- package/dist/auth/client.mjs +19 -0
- package/dist/auth/middleware.d.mts +54 -0
- package/dist/auth/middleware.mjs +152 -0
- package/dist/auth/server.d.mts +108 -0
- package/dist/auth/server.mjs +214 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/platform/app-switcher.d.mts +96 -0
- package/dist/platform/app-switcher.mjs +74 -0
- package/dist/shared.d.mts +19 -0
- package/dist/shared.mjs +14 -0
- package/dist/supabase/client.d.mts +8 -0
- package/dist/supabase/client.mjs +12 -0
- package/dist/supabase/server.d.mts +11 -0
- package/dist/supabase/server.mjs +25 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
HENOSIA COMMERCIAL SOURCE LICENSE
|
|
2
|
+
Version 1.0, May 20, 2026
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Henosia ApS. All rights reserved.
|
|
5
|
+
|
|
6
|
+
1. DEFINITIONS
|
|
7
|
+
|
|
8
|
+
"Licensor" means Henosia ApS, a Danish private limited company
|
|
9
|
+
(anpartsselskab) registered in Denmark as no. 42120014.
|
|
10
|
+
|
|
11
|
+
"Software" means the source code, object code, documentation, and any
|
|
12
|
+
other materials contained in this package, including any updates or
|
|
13
|
+
modifications provided by Licensor.
|
|
14
|
+
|
|
15
|
+
"Henosia Platform" means the software development and hosting platform
|
|
16
|
+
operated by Licensor at henosia.com (and successor domains).
|
|
17
|
+
|
|
18
|
+
"Authorized Customer" means a natural or legal person who holds an
|
|
19
|
+
active, paid subscription to the Henosia Platform and is in good
|
|
20
|
+
standing under Licensor's then-current terms of service as described
|
|
21
|
+
at https://www.henosia.com/terms-of-service.
|
|
22
|
+
|
|
23
|
+
"Customer Application" means an application developed by an Authorized
|
|
24
|
+
Customer on the Henosia Platform that incorporates the Software solely
|
|
25
|
+
to provide authentication functionality to that application's end
|
|
26
|
+
users.
|
|
27
|
+
|
|
28
|
+
"End User" means a natural person who interacts with a Customer
|
|
29
|
+
Application (for example, to sign in or authenticate) without
|
|
30
|
+
themselves redistributing or repackaging the Software.
|
|
31
|
+
|
|
32
|
+
"Competing Service" means any product, service, or platform that
|
|
33
|
+
provides, in whole or in part: (a) AI-assisted application generation;
|
|
34
|
+
(b) AI-assisted coding environments or IDEs; or (c) AI agent platforms
|
|
35
|
+
that produce, modify, or deploy software on behalf of users. This
|
|
36
|
+
includes any service that competes, directly or indirectly, with the
|
|
37
|
+
Henosia Platform.
|
|
38
|
+
|
|
39
|
+
2. GRANT OF LICENSE
|
|
40
|
+
|
|
41
|
+
Subject to the restrictions in Section 3 and continued compliance with
|
|
42
|
+
this License, Licensor grants Authorized Customers a worldwide,
|
|
43
|
+
non-exclusive, non-transferable, non-sublicensable, royalty-free,
|
|
44
|
+
revocable license to:
|
|
45
|
+
|
|
46
|
+
(a) install and use the Software, in unmodified form, solely as a
|
|
47
|
+
dependency of Customer Applications;
|
|
48
|
+
|
|
49
|
+
(b) distribute the Software, in unmodified form and only as bundled
|
|
50
|
+
within a Customer Application, to End Users to the extent
|
|
51
|
+
necessary for those End Users to use the Customer Application.
|
|
52
|
+
|
|
53
|
+
End Users receive no separate license to the Software and may use it
|
|
54
|
+
only as embedded in a Customer Application.
|
|
55
|
+
|
|
56
|
+
3. RESTRICTIONS
|
|
57
|
+
|
|
58
|
+
Except as expressly permitted in Section 2, you may not:
|
|
59
|
+
|
|
60
|
+
(a) use, install, copy, execute, or distribute the Software unless
|
|
61
|
+
you are an Authorized Customer or an End User of a Customer
|
|
62
|
+
Application;
|
|
63
|
+
|
|
64
|
+
(b) modify, adapt, translate, fork, or create derivative works of the
|
|
65
|
+
Software;
|
|
66
|
+
|
|
67
|
+
(c) use, incorporate, or make the Software available in connection
|
|
68
|
+
with the development, operation, training, or improvement of any
|
|
69
|
+
Competing Service;
|
|
70
|
+
|
|
71
|
+
(d) reverse engineer, decompile, disassemble, or otherwise attempt to
|
|
72
|
+
derive the source code, structure, or internal logic of the
|
|
73
|
+
Software, except and only to the extent that such activity is
|
|
74
|
+
expressly permitted by applicable mandatory law (including, where
|
|
75
|
+
applicable, Article 6 of EU Directive 2009/24/EC on the legal
|
|
76
|
+
protection of computer programs) notwithstanding this limitation;
|
|
77
|
+
|
|
78
|
+
(e) sublicense, sell, rent, lease, lend, or otherwise transfer the
|
|
79
|
+
Software to any third party except as part of a Customer
|
|
80
|
+
Application as permitted in Section 2;
|
|
81
|
+
|
|
82
|
+
(f) remove, alter, or obscure any copyright, trademark, or other
|
|
83
|
+
proprietary notices contained in the Software;
|
|
84
|
+
|
|
85
|
+
(g) use the name "Henosia," any Henosia logo, or any confusingly
|
|
86
|
+
similar mark, except as required to comply with the notice
|
|
87
|
+
requirements of this License.
|
|
88
|
+
|
|
89
|
+
4. OWNERSHIP
|
|
90
|
+
|
|
91
|
+
The Software is licensed, not sold. Licensor retains all right, title,
|
|
92
|
+
and interest in and to the Software, including all intellectual
|
|
93
|
+
property rights. No rights are granted by implication, estoppel, or
|
|
94
|
+
otherwise except as expressly set forth in this License. No patent
|
|
95
|
+
rights are granted under this License.
|
|
96
|
+
|
|
97
|
+
5. CUSTOMER APPLICATION CODE
|
|
98
|
+
|
|
99
|
+
For the avoidance of doubt, this License governs only the Software.
|
|
100
|
+
Code authored by an Authorized Customer for a Customer Application
|
|
101
|
+
using the Henosia Platform remains the property of that Authorized
|
|
102
|
+
Customer, subject to the Henosia Platform terms of service.
|
|
103
|
+
|
|
104
|
+
6. TERMINATION
|
|
105
|
+
|
|
106
|
+
This License terminates automatically and immediately, without notice,
|
|
107
|
+
if:
|
|
108
|
+
|
|
109
|
+
(a) you cease to be an Authorized Customer (for example, by
|
|
110
|
+
cancellation, non-payment, or suspension of your Henosia
|
|
111
|
+
subscription); or
|
|
112
|
+
|
|
113
|
+
(b) you breach any term of this License.
|
|
114
|
+
|
|
115
|
+
Upon termination, your rights under Section 2 cease. You must, within
|
|
116
|
+
thirty (30) days of termination ("Wind-Down Period"), remove the
|
|
117
|
+
Software from all Customer Applications, including from all source
|
|
118
|
+
code repositories, build artifacts, deployments, and dependency
|
|
119
|
+
manifests under your control. After the Wind-Down Period, you may
|
|
120
|
+
not install, execute, distribute, or otherwise use the Software in
|
|
121
|
+
any form.
|
|
122
|
+
|
|
123
|
+
For the avoidance of doubt, this License does not affect your
|
|
124
|
+
ownership of, or your right to continue using, the application code
|
|
125
|
+
you authored for a Customer Application. You remain free to replace
|
|
126
|
+
the functionality previously provided by the Software with your own
|
|
127
|
+
implementation or with a third-party alternative.
|
|
128
|
+
|
|
129
|
+
During the Wind-Down Period only, End Users may continue to use
|
|
130
|
+
Customer Applications into which the Software has already been
|
|
131
|
+
incorporated and deployed. After the Wind-Down Period, the former
|
|
132
|
+
Authorized Customer must ensure the Software is no longer present in
|
|
133
|
+
any deployed Customer Application.
|
|
134
|
+
|
|
135
|
+
Sections 3, 4, 7, 8, 9, and 10 survive termination.
|
|
136
|
+
|
|
137
|
+
7. DISCLAIMER OF WARRANTY
|
|
138
|
+
|
|
139
|
+
THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND,
|
|
140
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
141
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
|
142
|
+
NON-INFRINGEMENT. LICENSOR DOES NOT WARRANT THAT THE SOFTWARE WILL BE
|
|
143
|
+
ERROR-FREE OR UNINTERRUPTED.
|
|
144
|
+
|
|
145
|
+
8. LIMITATION OF LIABILITY
|
|
146
|
+
|
|
147
|
+
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
|
|
148
|
+
LICENSOR BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
|
|
149
|
+
CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR ANY LOSS OF PROFITS, REVENUE,
|
|
150
|
+
DATA, OR GOODWILL, ARISING OUT OF OR IN CONNECTION WITH THIS LICENSE
|
|
151
|
+
OR THE USE OF OR INABILITY TO USE THE SOFTWARE, WHETHER BASED ON
|
|
152
|
+
CONTRACT, TORT, OR ANY OTHER LEGAL THEORY, EVEN IF LICENSOR HAS BEEN
|
|
153
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. LICENSOR'S TOTAL AGGREGATE
|
|
154
|
+
LIABILITY UNDER THIS LICENSE SHALL NOT EXCEED THE TOTAL SUBSCRIPTION
|
|
155
|
+
FEES PAID BY THE AUTHORIZED CUSTOMER TO LICENSOR FOR THE HENOSIA
|
|
156
|
+
PLATFORM DURING THE TWELVE (12) MONTHS IMMEDIATELY PRECEDING THE EVENT
|
|
157
|
+
GIVING RISE TO THE CLAIM, OR EUR 100, WHICHEVER IS GREATER.
|
|
158
|
+
|
|
159
|
+
Nothing in this License limits or excludes liability that cannot be
|
|
160
|
+
limited or excluded under applicable mandatory law.
|
|
161
|
+
|
|
162
|
+
9. GOVERNING LAW AND JURISDICTION
|
|
163
|
+
|
|
164
|
+
This License is governed by and construed in accordance with the laws
|
|
165
|
+
of Denmark, excluding its conflict-of-laws rules and excluding the
|
|
166
|
+
United Nations Convention on Contracts for the International Sale of
|
|
167
|
+
Goods. The exclusive venue for any dispute arising out of or relating
|
|
168
|
+
to this License shall be the courts of Copenhagen, Denmark.
|
|
169
|
+
|
|
170
|
+
10. GENERAL
|
|
171
|
+
|
|
172
|
+
If any provision of this License is held to be unenforceable, the
|
|
173
|
+
remaining provisions shall remain in full force and effect. Failure
|
|
174
|
+
to enforce any provision is not a waiver of future enforcement.
|
|
175
|
+
Licensor may publish updated versions of this License for future
|
|
176
|
+
releases of the Software. The version of this License accompanying a
|
|
177
|
+
given release of the Software governs that release; updates do not
|
|
178
|
+
retroactively apply to releases made before the update. This License
|
|
179
|
+
constitutes the entire agreement between you and Licensor regarding
|
|
180
|
+
the Software and supersedes any prior agreements relating to its
|
|
181
|
+
subject matter.
|
|
182
|
+
|
|
183
|
+
For licensing inquiries: hello@henosia.com
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# @henosia/app-next
|
|
2
|
+
|
|
3
|
+
Henosia integration for Next.js apps: Provides middleware and better-auth setup for Henosia Auth, Henosia Platform API route handlers, and (optionally) automatic Supabase sign in.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @henosia/app-next better-auth next react react-dom @tanstack/react-query
|
|
9
|
+
# Optional, only if your app uses Supabase
|
|
10
|
+
pnpm add @supabase/ssr @supabase/supabase-js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Peer dependencies
|
|
14
|
+
|
|
15
|
+
| Package | Required range | Notes |
|
|
16
|
+
|--------------------------|-------------------------|-------------------------------------------|
|
|
17
|
+
| `next` | `14.x \| 15.x \| 16.x` | |
|
|
18
|
+
| `better-auth` | `>=1.6.0 <2` | |
|
|
19
|
+
| `react` / `react-dom` | `^18 \| ^19` | Required by `better-auth/react` |
|
|
20
|
+
| `@tanstack/react-query` | `^5.0.0` | Required by the app switcher |
|
|
21
|
+
| `@supabase/ssr` | `^0.10.2` | Optional — only when integrating Supabase |
|
|
22
|
+
| `@supabase/supabase-js` | `^2.103.0` | Optional — only when integrating Supabase |
|
|
23
|
+
|
|
24
|
+
## Required environment variables
|
|
25
|
+
|
|
26
|
+
| Variable | Description |
|
|
27
|
+
|---------------------------------------|-------------------------------------------------------|
|
|
28
|
+
| `BETTER_AUTH_URL` | The consuming app's deployment/preview base URL |
|
|
29
|
+
| `BETTER_AUTH_SECRET` | The consuming app's better-auth secret |
|
|
30
|
+
| `HENOSIA_AUTH_SERVICE_BASE_URL` | Henosia auth/platform service base URL |
|
|
31
|
+
| `HENOSIA_AUTH_CLIENT_ID` | OAuth client ID for this project |
|
|
32
|
+
| `HENOSIA_AUTH_CLIENT_SECRET` | OAuth client secret for this project |
|
|
33
|
+
| `HENOSIA_AUTH_PROJECT_ID` | Project ID validated against the JWT project claim |
|
|
34
|
+
| `HENOSIA_AUTH_SUPABASE_PROVIDER` | Optional — defaults to `custom:henosia-auth:platform` |
|
|
35
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | Optional — enables Supabase session exchange when set |
|
|
36
|
+
| `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`| Required when `NEXT_PUBLIC_SUPABASE_URL` is set |
|
|
37
|
+
|
|
38
|
+
## Setup
|
|
39
|
+
|
|
40
|
+
### 1. Middleware
|
|
41
|
+
|
|
42
|
+
Create (or edit) `middleware.ts` in your app root:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// middleware.ts
|
|
46
|
+
import { type NextRequest } from 'next/server'
|
|
47
|
+
import { createHenosiaAuthMiddleware } from '@henosia/app-next/auth/middleware'
|
|
48
|
+
|
|
49
|
+
const henosiaAuth = createHenosiaAuthMiddleware()
|
|
50
|
+
|
|
51
|
+
export async function middleware(request: NextRequest) {
|
|
52
|
+
// App-specific pre-checks here, if any.
|
|
53
|
+
return await henosiaAuth(request)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const config = {
|
|
57
|
+
matcher: [
|
|
58
|
+
/*
|
|
59
|
+
* Match all request paths except for the ones starting with:
|
|
60
|
+
* - _next/static (static files)
|
|
61
|
+
* - _next/image (image optimization files)
|
|
62
|
+
* - favicon.ico (favicon file)
|
|
63
|
+
*/
|
|
64
|
+
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If you want to add additional public paths that should bypass the auth pre-check:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const henosiaAuth = createHenosiaAuthMiddleware({
|
|
73
|
+
additionalUnauthenticatedPathNames: ['/health'],
|
|
74
|
+
additionalUnauthenticatedPathNamePrefixes: ['/public/'],
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. better-auth catch-all route
|
|
79
|
+
|
|
80
|
+
Create `app/api/auth/[...all]/route.ts`:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export { GET, POST } from '@henosia/app-next/api/auth'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3. Henosia Platform catch-all route
|
|
87
|
+
|
|
88
|
+
Create `app/api/henosia-platform/[...all]/route.ts`:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
export { GET } from '@henosia/app-next/api/platform'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 4. React auth client
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// e.g. lib/auth-client.ts
|
|
98
|
+
export { authClient } from '@henosia/app-next/auth/client'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 5. (Optional) Supabase helpers
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// Server Components / Route Handlers
|
|
105
|
+
import { createClient } from '@henosia/app-next/supabase/server'
|
|
106
|
+
|
|
107
|
+
// Client Components
|
|
108
|
+
import { createClient } from '@henosia/app-next/supabase/client'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Server-side helpers
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import {
|
|
115
|
+
auth,
|
|
116
|
+
verifyHenosiaAuthToken,
|
|
117
|
+
isUnauthorizedException,
|
|
118
|
+
isPageRequest,
|
|
119
|
+
HENOSIA_AUTH_SIGN_IN_PATH_NAME,
|
|
120
|
+
} from '@henosia/app-next'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`verifyHenosiaAuthToken(request)` MUST be called from any protected page/route handler/server action — the
|
|
124
|
+
middleware only performs a fast pre-check.
|
|
125
|
+
|
|
126
|
+
## App-switcher hook
|
|
127
|
+
|
|
128
|
+
`@henosia/app-next/platform/app-switcher` exposes the `useAppSwitcher` React hook used to build a custom
|
|
129
|
+
app-switcher UI. It reads the current organization context from the cookie set by the middleware and queries
|
|
130
|
+
the catch-all platform route mounted in [step 3](#3-henosia-platform-catch-all-route).
|
|
131
|
+
|
|
132
|
+
Requires a `@tanstack/react-query` `QueryClientProvider` higher up in the tree.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
'use client'
|
|
136
|
+
|
|
137
|
+
import { useState } from 'react'
|
|
138
|
+
import { useAppSwitcher } from '@henosia/app-next/platform/app-switcher'
|
|
139
|
+
|
|
140
|
+
export function MyAppSwitcher() {
|
|
141
|
+
const [open, setOpen] = useState(false)
|
|
142
|
+
const { organization, groupedApps, isLoading, error } = useAppSwitcher({
|
|
143
|
+
enabled: open
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Render your own Popover/Command/menu UI using `organization`, `groupedApps`,
|
|
147
|
+
// `isLoading`, and `error`.
|
|
148
|
+
return (
|
|
149
|
+
<button onClick={() => setOpen((v) => !v)}>
|
|
150
|
+
{organization?.name ?? 'Loading…'}
|
|
151
|
+
</button>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Option | Type | Default | Description |
|
|
157
|
+
|---------------|---------------|---------|----------------------------------------------------------------------------------------------------------------------------|
|
|
158
|
+
| `enabled` | `boolean` | `true` | Whether the underlying query should run. Wire to e.g. a popover's open state to defer it. |
|
|
159
|
+
|
|
160
|
+
## Subpath exports
|
|
161
|
+
|
|
162
|
+
| Subpath | Purpose |
|
|
163
|
+
|------------------------------------------------|------------------------------------------------------------|
|
|
164
|
+
| `@henosia/app-next` | Server-side helpers (`auth`, `verifyHenosiaAuthToken`, …) |
|
|
165
|
+
| `@henosia/app-next/shared` | Shared constants & types (no runtime deps) |
|
|
166
|
+
| `@henosia/app-next/auth/middleware` | `createHenosiaAuthMiddleware` |
|
|
167
|
+
| `@henosia/app-next/auth/server` | Lower-level server-side auth surface (re-exported by root) |
|
|
168
|
+
| `@henosia/app-next/auth/client` | Better-auth `authClient` for React |
|
|
169
|
+
| `@henosia/app-next/api/auth` | `GET`, `POST` for `/api/auth/[...all]` |
|
|
170
|
+
| `@henosia/app-next/api/platform` | `GET` for `/api/henosia-platform/[...all]` |
|
|
171
|
+
| `@henosia/app-next/platform/app-switcher` | `useAppSwitcher` React hook |
|
|
172
|
+
| `@henosia/app-next/supabase/server` | Server-side Supabase client factory |
|
|
173
|
+
| `@henosia/app-next/supabase/client` | Browser Supabase client factory |
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*! Copyright (c) 2026 Henosia ApS. Licensed under the Henosia Commercial Source License v1.0. See LICENSE */
|
|
2
|
+
import { auth } from "../auth/server.mjs";
|
|
3
|
+
import { toNextJsHandler } from "better-auth/next-js";
|
|
4
|
+
//#region src/api/auth.ts
|
|
5
|
+
/**
|
|
6
|
+
* Catch-all Next.js route handler for better-auth.
|
|
7
|
+
*
|
|
8
|
+
* Mount as a catch-all route at `app/api/auth/[...all]/route.ts`:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* export { GET, POST } from '@henosia/app-next/api/auth'
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
const handlers = toNextJsHandler(auth);
|
|
15
|
+
const GET = handlers.GET;
|
|
16
|
+
const POST = handlers.POST;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { GET, POST };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
import { NextRequest } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
//#region src/api/platform.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Catch-all Next.js route handler for the Henosia Platform API routes.
|
|
7
|
+
*
|
|
8
|
+
* Mount as a catch-all route at `app/api/henosia-platform/[...all]/route.ts`:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* export { GET } from '@henosia/app-next/api/platform'
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Currently routes:
|
|
15
|
+
* - `GET /api/henosia-platform/v1/app-switcher` → `handleAppSwitcher`
|
|
16
|
+
*/
|
|
17
|
+
declare function GET(request: NextRequest): Promise<Response>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { GET };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*! Copyright (c) 2026 Henosia ApS. Licensed under the Henosia Commercial Source License v1.0. See LICENSE */
|
|
2
|
+
import { henosiaAuthConfig, isUnauthorizedException, verifyHenosiaAuthToken } from "../auth/server.mjs";
|
|
3
|
+
//#region src/api/platform/v1/app-switcher.ts
|
|
4
|
+
/**
|
|
5
|
+
* Handler for the Henosia Platform `app-switcher` endpoint.
|
|
6
|
+
* Proxies the authenticated request to the Henosia Auth platform service using the verified id token.
|
|
7
|
+
*/
|
|
8
|
+
async function handleAppSwitcher(request) {
|
|
9
|
+
try {
|
|
10
|
+
const { accessTokenResponse } = await verifyHenosiaAuthToken(request);
|
|
11
|
+
const fetchUrl = new URL(henosiaAuthConfig.henosiaAuthPlatformServiceBaseUrl.get());
|
|
12
|
+
fetchUrl.pathname = request.nextUrl.pathname;
|
|
13
|
+
const response = await fetch(fetchUrl, {
|
|
14
|
+
method: "GET",
|
|
15
|
+
redirect: "error",
|
|
16
|
+
headers: { authorization: `Bearer ${accessTokenResponse.idToken}` }
|
|
17
|
+
});
|
|
18
|
+
return Response.json(await response.json(), {
|
|
19
|
+
status: response.status,
|
|
20
|
+
statusText: response.statusText
|
|
21
|
+
});
|
|
22
|
+
} catch (e) {
|
|
23
|
+
if (isUnauthorizedException(e)) return Response.json({ error: "Your session is invalid or expired. Please reload the page to sign in again." }, { status: 401 });
|
|
24
|
+
console.error("[Henosia Platform] App Switcher API error", e);
|
|
25
|
+
return Response.json({ error: "Server error" }, { status: 500 });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/api/platform.ts
|
|
30
|
+
/**
|
|
31
|
+
* Catch-all Next.js route handler for the Henosia Platform API routes.
|
|
32
|
+
*
|
|
33
|
+
* Mount as a catch-all route at `app/api/henosia-platform/[...all]/route.ts`:
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* export { GET } from '@henosia/app-next/api/platform'
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Currently routes:
|
|
40
|
+
* - `GET /api/henosia-platform/v1/app-switcher` → `handleAppSwitcher`
|
|
41
|
+
*/
|
|
42
|
+
async function GET(request) {
|
|
43
|
+
const { pathname } = request.nextUrl;
|
|
44
|
+
if (pathname === "/api/henosia-platform/v1/app-switcher") return handleAppSwitcher(request);
|
|
45
|
+
return Response.json({ error: `Not found: ${pathname}` }, { status: 404 });
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { GET };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
import { createAuthClient } from "better-auth/react";
|
|
3
|
+
|
|
4
|
+
//#region src/auth/client.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal React auth client type, derived from `createAuthClient` with no plugins.
|
|
7
|
+
*
|
|
8
|
+
* The full plugin-aware return type of `createAuthClient<Option>()` is a deeply-inferred graph that
|
|
9
|
+
* references non-portable internals from `better-auth` and `zod` and cannot be named in published
|
|
10
|
+
* declarations (TS2883). By widening to the no-plugin shape we keep the supported public surface
|
|
11
|
+
* (`signIn`, `signOut`, `useSession`, `getSession`, `$Infer`, `$fetch`, etc.) strongly typed at the
|
|
12
|
+
* package boundary while keeping the emitted `.d.mts` portable.
|
|
13
|
+
*/
|
|
14
|
+
type HenosiaAuthClient = ReturnType<typeof createAuthClient>;
|
|
15
|
+
/**
|
|
16
|
+
* The Henosia Auth client for use in browser/React contexts.
|
|
17
|
+
*
|
|
18
|
+
* Uses better-auth's React client with the Henosia Auth plugins (OAuth + JWT).
|
|
19
|
+
*
|
|
20
|
+
* Typed as the minimal {@link HenosiaAuthClient} (the no-plugin React client shape) so consumers get
|
|
21
|
+
* proper IntelliSense for the standard better-auth methods/hooks.
|
|
22
|
+
*/
|
|
23
|
+
declare const authClient: HenosiaAuthClient;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { HenosiaAuthClient, authClient };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*! Copyright (c) 2026 Henosia ApS. Licensed under the Henosia Commercial Source License v1.0. See LICENSE */
|
|
2
|
+
"use client";
|
|
3
|
+
import { createAuthClient } from "better-auth/react";
|
|
4
|
+
import { genericOAuthClient, jwtClient } from "better-auth/client/plugins";
|
|
5
|
+
//#region src/auth/client.ts
|
|
6
|
+
/**
|
|
7
|
+
* The Henosia Auth client for use in browser/React contexts.
|
|
8
|
+
*
|
|
9
|
+
* Uses better-auth's React client with the Henosia Auth plugins (OAuth + JWT).
|
|
10
|
+
*
|
|
11
|
+
* Typed as the minimal {@link HenosiaAuthClient} (the no-plugin React client shape) so consumers get
|
|
12
|
+
* proper IntelliSense for the standard better-auth methods/hooks.
|
|
13
|
+
*/
|
|
14
|
+
const authClient = createAuthClient({
|
|
15
|
+
plugins: [jwtClient(), genericOAuthClient()],
|
|
16
|
+
sessionOptions: { refetchInterval: 600 }
|
|
17
|
+
});
|
|
18
|
+
//#endregion
|
|
19
|
+
export { authClient };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
import { NextRequest, NextResponse } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
//#region src/auth/middleware.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link createHenosiaAuthMiddleware}.
|
|
7
|
+
*/
|
|
8
|
+
interface HenosiaAuthMiddlewareOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Additional URL pathname values that should not check for auth in middleware.
|
|
11
|
+
* These are merged with the default {@link HENOSIA_AUTH_MIDDLEWARE_UNAUTHENTICATED_PATH_NAMES}.
|
|
12
|
+
*
|
|
13
|
+
* Use ONLY for paths that should not be intercepted by the middleware auth pre-check and redirect.
|
|
14
|
+
*/
|
|
15
|
+
additionalUnauthenticatedPathNames?: ReadonlyArray<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Additional URL pathname prefixes that should not be blocked by auth in middleware.
|
|
18
|
+
* Each value must end with `/` to prevent unexpected partial route matches.
|
|
19
|
+
* These are merged with the default {@link HENOSIA_AUTH_MIDDLEWARE_UNAUTHENTICATED_PATH_NAME_PREFIXES}.
|
|
20
|
+
*
|
|
21
|
+
* Use ONLY for paths that should not be intercepted by the middleware auth pre-check and redirect.
|
|
22
|
+
*/
|
|
23
|
+
additionalUnauthenticatedPathNamePrefixes?: ReadonlyArray<string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Next.js middleware function that performs the Henosia Auth pre-check.
|
|
27
|
+
*
|
|
28
|
+
* The returned function:
|
|
29
|
+
* - Returns a redirect to the sign-in page when no valid session cookie is present.
|
|
30
|
+
* - Verifies the Henosia Auth JWT, transferring any refreshed cookies onto the response.
|
|
31
|
+
* - Optionally exchanges the Henosia Auth token for a Supabase session if `NEXT_PUBLIC_SUPABASE_URL` is set.
|
|
32
|
+
* - Returns the resulting `NextResponse` (a `NextResponse.next({ request })` when authenticated, a redirect or
|
|
33
|
+
* 401 JSON response otherwise) so the caller can compose additional logic on top.
|
|
34
|
+
*
|
|
35
|
+
* **Important**: this middleware performs an auth *pre-check*. Authorization MUST still be performed at the
|
|
36
|
+
* relevant pages and route handlers using {@link verifyHenosiaAuthToken}.
|
|
37
|
+
*
|
|
38
|
+
* @example Composing with custom logic
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { type NextRequest, NextResponse } from 'next/server'
|
|
41
|
+
* import { createHenosiaAuthMiddleware } from '@henosia/app-next/middleware'
|
|
42
|
+
*
|
|
43
|
+
* const henosiaAuth = createHenosiaAuthMiddleware()
|
|
44
|
+
*
|
|
45
|
+
* export async function middleware(request: NextRequest) {
|
|
46
|
+
* // Add app-specific logic here ...
|
|
47
|
+
* return await henosiaAuth(request)
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function createHenosiaAuthMiddleware(options?: HenosiaAuthMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { HenosiaAuthMiddlewareOptions, createHenosiaAuthMiddleware };
|