@_mustachio/openauth 0.13.1 → 0.13.3
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/dist/esm/domain/authorize.js +11 -3
- package/dist/esm/domain/callback.js +9 -2
- package/dist/esm/domain/method-dispatch.js +1 -0
- package/dist/esm/domain/method-route.js +2 -0
- package/dist/esm/domain/mount.js +29 -0
- package/dist/esm/http/handlers/method-route.js +8 -3
- package/dist/esm/index.js +3 -0
- package/dist/esm/methods/code.js +9 -8
- package/dist/esm/methods/oauth2-generic.js +2 -1
- package/dist/esm/methods/passkey.js +3 -2
- package/dist/esm/methods/password.js +9 -8
- package/dist/esm/methods/saml-sp/metadata.js +2 -1
- package/dist/types/domain/authorize.d.ts.map +1 -1
- package/dist/types/domain/callback.d.ts +6 -5
- package/dist/types/domain/callback.d.ts.map +1 -1
- package/dist/types/domain/method-dispatch.d.ts +5 -0
- package/dist/types/domain/method-dispatch.d.ts.map +1 -1
- package/dist/types/domain/method-route.d.ts +6 -0
- package/dist/types/domain/method-route.d.ts.map +1 -1
- package/dist/types/domain/mount.d.ts +90 -0
- package/dist/types/domain/mount.d.ts.map +1 -0
- package/dist/types/http/handlers/method-route.d.ts.map +1 -1
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/methods/code.d.ts.map +1 -1
- package/dist/types/methods/oauth2-generic.d.ts.map +1 -1
- package/dist/types/methods/passkey.d.ts.map +1 -1
- package/dist/types/methods/password.d.ts.map +1 -1
- package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +16 -1
- package/dist/types/types/idp.d.ts.map +1 -1
- package/dist/types/types/method.d.ts +11 -0
- package/dist/types/types/method.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/domain/authorize.ts +15 -4
- package/src/domain/callback.ts +18 -7
- package/src/domain/method-dispatch.ts +6 -0
- package/src/domain/method-route.ts +8 -0
- package/src/domain/mount.ts +113 -0
- package/src/http/handlers/method-route.ts +9 -5
- package/src/index.ts +17 -0
- package/src/methods/code.ts +15 -6
- package/src/methods/oauth2-generic.ts +6 -1
- package/src/methods/passkey.ts +6 -2
- package/src/methods/password.ts +19 -4
- package/src/methods/saml-sp/metadata.ts +5 -1
- package/src/types/idp.ts +16 -1
- package/src/types/method.ts +11 -0
package/src/methods/password.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
MethodContext,
|
|
27
27
|
MethodResult,
|
|
28
28
|
} from "../types/method"
|
|
29
|
+
import { mountedPath } from "../domain/mount"
|
|
29
30
|
import { renderForm } from "../ui/forms"
|
|
30
31
|
|
|
31
32
|
export type PasswordProperties = {
|
|
@@ -140,7 +141,7 @@ async function renderLogin(
|
|
|
140
141
|
): Promise<MethodResult<PasswordProperties, PasswordState>> {
|
|
141
142
|
const body = renderForm({
|
|
142
143
|
title,
|
|
143
|
-
action: `/m/${methodId}/login
|
|
144
|
+
action: mountedPath(ctx.issuerUrl, `/m/${methodId}/login`),
|
|
144
145
|
fields: [
|
|
145
146
|
{
|
|
146
147
|
name: "email",
|
|
@@ -180,6 +181,7 @@ async function handleLogin(
|
|
|
180
181
|
const parsed = loginBodySchema.safeParse(form)
|
|
181
182
|
if (!parsed.success) {
|
|
182
183
|
return reLoginWithError(
|
|
184
|
+
ctx.issuerUrl,
|
|
183
185
|
methodId,
|
|
184
186
|
title,
|
|
185
187
|
"Please enter your email and password.",
|
|
@@ -188,12 +190,22 @@ async function handleLogin(
|
|
|
188
190
|
|
|
189
191
|
const user = await users.findByEmail(parsed.data.email, ctx.tenant.id)
|
|
190
192
|
if (!user) {
|
|
191
|
-
return reLoginWithError(
|
|
193
|
+
return reLoginWithError(
|
|
194
|
+
ctx.issuerUrl,
|
|
195
|
+
methodId,
|
|
196
|
+
title,
|
|
197
|
+
"Invalid email or password.",
|
|
198
|
+
)
|
|
192
199
|
}
|
|
193
200
|
|
|
194
201
|
const verified = await hasher.verify(parsed.data.password, user.passwordHash)
|
|
195
202
|
if (!verified) {
|
|
196
|
-
return reLoginWithError(
|
|
203
|
+
return reLoginWithError(
|
|
204
|
+
ctx.issuerUrl,
|
|
205
|
+
methodId,
|
|
206
|
+
title,
|
|
207
|
+
"Invalid email or password.",
|
|
208
|
+
)
|
|
197
209
|
}
|
|
198
210
|
|
|
199
211
|
return {
|
|
@@ -218,6 +230,7 @@ async function handleRegister(
|
|
|
218
230
|
const parsed = registerBodySchema.safeParse(form)
|
|
219
231
|
if (!parsed.success) {
|
|
220
232
|
return reLoginWithError(
|
|
233
|
+
ctx.issuerUrl,
|
|
221
234
|
methodId,
|
|
222
235
|
title,
|
|
223
236
|
"Password must be at least 8 characters and email must be valid.",
|
|
@@ -227,6 +240,7 @@ async function handleRegister(
|
|
|
227
240
|
const existing = await users.findByEmail(parsed.data.email, ctx.tenant.id)
|
|
228
241
|
if (existing) {
|
|
229
242
|
return reLoginWithError(
|
|
243
|
+
ctx.issuerUrl,
|
|
230
244
|
methodId,
|
|
231
245
|
title,
|
|
232
246
|
"An account with that email already exists.",
|
|
@@ -250,6 +264,7 @@ async function handleRegister(
|
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
function reLoginWithError(
|
|
267
|
+
issuerUrl: string,
|
|
253
268
|
methodId: string,
|
|
254
269
|
title: string,
|
|
255
270
|
error: string,
|
|
@@ -259,7 +274,7 @@ function reLoginWithError(
|
|
|
259
274
|
response: new Response(
|
|
260
275
|
renderForm({
|
|
261
276
|
title,
|
|
262
|
-
action: `/m/${methodId}/login
|
|
277
|
+
action: mountedPath(issuerUrl, `/m/${methodId}/login`),
|
|
263
278
|
fields: [
|
|
264
279
|
{
|
|
265
280
|
name: "email",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* `WantAssertionsSigned` are both read from config, so they state
|
|
23
23
|
* actual runtime behaviour rather than a hardcoded assumption.
|
|
24
24
|
*/
|
|
25
|
+
import { mountedPath } from "../../domain/mount"
|
|
25
26
|
import { authError } from "../../types/error"
|
|
26
27
|
import type { MethodContext, MethodResult } from "../../types/method"
|
|
27
28
|
|
|
@@ -184,7 +185,10 @@ export async function buildSpMetadata(
|
|
|
184
185
|
// served (idp.sloUrl set ⇒ /sls is in publicRoutes). Derived from the
|
|
185
186
|
// same dispatch input as the ACS so it cannot drift.
|
|
186
187
|
const cb = new URL(ctx.dispatch.callbackUrl)
|
|
187
|
-
const slsUrl = `${cb.protocol}//${cb.host}
|
|
188
|
+
const slsUrl = `${cb.protocol}//${cb.host}${mountedPath(
|
|
189
|
+
ctx.dispatch.issuerUrl,
|
|
190
|
+
`/m/${methodId}/sls`,
|
|
191
|
+
)}`
|
|
188
192
|
const xml = buildSpMetadataXml({
|
|
189
193
|
spEntityId,
|
|
190
194
|
acsUrl: ctx.dispatch.callbackUrl,
|
package/src/types/idp.ts
CHANGED
|
@@ -255,7 +255,22 @@ export type IdPOptions = {
|
|
|
255
255
|
*/
|
|
256
256
|
resolveTenant: (req: Request) => Promise<Result<TenantId, AuthError>>
|
|
257
257
|
|
|
258
|
-
/**
|
|
258
|
+
/**
|
|
259
|
+
* Opt-in for partitioned callback hosts (recovery mechanism #2).
|
|
260
|
+
*
|
|
261
|
+
* Returns the hostname a given tenant's callbacks arrive on, so the
|
|
262
|
+
* tenant is recoverable from the `Host` header before the state
|
|
263
|
+
* envelope is verified.
|
|
264
|
+
*
|
|
265
|
+
* **The issuer's mount prefix still applies to the returned host.** If
|
|
266
|
+
* `issuerUrl` is `https://example.com/idp`, a tenant host of
|
|
267
|
+
* `acme.example.com` produces `https://acme.example.com/idp/cb/<id>`.
|
|
268
|
+
* This option varies the *authority* of one deployment — those hosts
|
|
269
|
+
* are served by this same service behind the same proxy, so they share
|
|
270
|
+
* its mount. A deployment needing partitioned hosts mounted differently
|
|
271
|
+
* from the issuer is describing two mounts, which one `issuerUrl`
|
|
272
|
+
* cannot express; a second option must not be added to paper over it.
|
|
273
|
+
*/
|
|
259
274
|
callbackHostFor?: (tenantId: TenantId) => string
|
|
260
275
|
|
|
261
276
|
/**
|
package/src/types/method.ts
CHANGED
|
@@ -113,6 +113,17 @@ export type MethodHandler<P, S> = (
|
|
|
113
113
|
export type MethodContext<S = unknown> = {
|
|
114
114
|
/** The raw Web Fetch `Request`. Web standard, not Hono `Context`. */
|
|
115
115
|
request: Request
|
|
116
|
+
/**
|
|
117
|
+
* The issuer URL of this IdP, always present.
|
|
118
|
+
*
|
|
119
|
+
* Distinct from `dispatch.issuerUrl`, which is only populated for
|
|
120
|
+
* `GET /authorize`. Methods that render their own URLs — a form action
|
|
121
|
+
* re-rendered on a validation error, say — need it on every route, and
|
|
122
|
+
* must build those URLs with `mountedPath` so they carry the
|
|
123
|
+
* deployment's mount prefix. `ctx.request.url` is not a substitute: a
|
|
124
|
+
* proxy-stripped inbound URL has already lost that prefix.
|
|
125
|
+
*/
|
|
126
|
+
issuerUrl: string
|
|
116
127
|
/** Path within the method's mount, e.g. `"/callback"` (without the `/<id>` prefix). */
|
|
117
128
|
subPath: string
|
|
118
129
|
tenant: TenantContext
|