@authup/client-auth-console 1.0.0-beta.62 → 1.0.0-beta.63

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.
@@ -12,7 +12,7 @@
12
12
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
13
13
  <title>Authup</title>
14
14
  <!--preload-links-->
15
- <script type="module" crossorigin src="/public/assets/index-D-9Xc0Mb.js"></script>
15
+ <script type="module" crossorigin src="/public/assets/index-D9LuwbLi.js"></script>
16
16
  <link rel="stylesheet" crossorigin href="/public/assets/index-8yjUEagG.css">
17
17
  </head>
18
18
  <body>
@@ -32943,7 +32943,7 @@ function isAuthupError(input) {
32943
32943
  if (!Array.isArray(input["@instanceof"])) return false;
32944
32944
  return matchesInstanceof(input, AUTHUP_ERROR_INSTANCE);
32945
32945
  }
32946
- ErrorCode$1.OAUTH_CLIENT_INVALID, ErrorCode$1.HTTP_BEARER_TOKEN_MALFORMED, ErrorCode$1.HTTP_HEADER_AUTH_TYPE_UNSUPPORTED, ErrorCode$1.IDENTITY_UNAUTHORIZED, ErrorCode$1.PERMISSION_NOT_FOUND, ErrorCode$1.PERMISSION_DENIED, ErrorCode$1.PERMISSION_EVALUATION_FAILED, ErrorCode$1.ENTITY_NOT_FOUND, ErrorCode$1.JWK_NOT_FOUND, ErrorCode$1.ENTITY_CONFLICT, ErrorCode$1.LOGIN_ATTEMPT_THROTTLED, ErrorCode$1.MFA_ATTEMPT_THROTTLED, ErrorCode$1.INTERNAL_ERROR, ErrorCode$1.UPSTREAM_ERROR, ErrorCode$1.STORAGE_INSUFFICIENT;
32946
+ ErrorCode$1.OAUTH_CLIENT_INVALID, ErrorCode$1.HTTP_BEARER_TOKEN_MALFORMED, ErrorCode$1.HTTP_HEADER_AUTH_TYPE_UNSUPPORTED, ErrorCode$1.IDENTITY_UNAUTHORIZED, ErrorCode$1.JWT_EXPIRED, ErrorCode$1.JWT_INACTIVE, ErrorCode$1.JWT_INVALID, ErrorCode$1.PERMISSION_NOT_FOUND, ErrorCode$1.PERMISSION_DENIED, ErrorCode$1.PERMISSION_EVALUATION_FAILED, ErrorCode$1.ENTITY_NOT_FOUND, ErrorCode$1.JWK_NOT_FOUND, ErrorCode$1.ENTITY_CONFLICT, ErrorCode$1.LOGIN_ATTEMPT_THROTTLED, ErrorCode$1.MFA_ATTEMPT_THROTTLED, ErrorCode$1.INTERNAL_ERROR, ErrorCode$1.UPSTREAM_ERROR, ErrorCode$1.STORAGE_INSUFFICIENT;
32947
32947
  //#endregion
32948
32948
  //#region ../../packages/core-kit/src/helpers/name-valid.ts
32949
32949
  function isNameValid(input, options = {}) {
@@ -47475,6 +47475,8 @@ var IdentityProviderOAuth2AttributesValidator = class extends Container {
47475
47475
  this.mount("authorizeUrl", createValidator(url$1()));
47476
47476
  this.mount("userInfoUrl", { optional: true }, createValidator(url$1().optional().nullable()));
47477
47477
  this.mount("scope", { optional: true }, createValidator(string$1().min(3).max(2e3).optional().nullable()));
47478
+ this.mount("requiredAmr", { optional: true }, createValidator(string$1().min(1).max(2e3).regex(/[^\s,]/).optional().nullable()));
47479
+ this.mount("requiredAcr", { optional: true }, createValidator(string$1().min(1).max(2e3).regex(/[^\s,]/).optional().nullable()));
47478
47480
  }
47479
47481
  };
47480
47482
  //#endregion
@@ -49212,6 +49214,10 @@ var TranslatorTranslationFieldKey = /* @__PURE__ */ function(TranslatorTranslati
49212
49214
  TranslatorTranslationFieldKey["CLIENT_ID"] = "clientId";
49213
49215
  TranslatorTranslationFieldKey["CLIENT_SECRET"] = "clientSecret";
49214
49216
  TranslatorTranslationFieldKey["SCOPE"] = "scope";
49217
+ TranslatorTranslationFieldKey["REQUIRED_AMR"] = "requiredAmr";
49218
+ TranslatorTranslationFieldKey["REQUIRED_AMR_HINT"] = "requiredAmrHint";
49219
+ TranslatorTranslationFieldKey["REQUIRED_ACR"] = "requiredAcr";
49220
+ TranslatorTranslationFieldKey["REQUIRED_ACR_HINT"] = "requiredAcrHint";
49215
49221
  TranslatorTranslationFieldKey["TOKEN"] = "token";
49216
49222
  TranslatorTranslationFieldKey["USER_INFO"] = "userInfo";
49217
49223
  TranslatorTranslationFieldKey["DISCOVERY"] = "discovery";
@@ -49251,6 +49257,7 @@ var TranslatorTranslationActionKey = /* @__PURE__ */ function(TranslatorTranslat
49251
49257
  TranslatorTranslationActionKey["SHOW"] = "show";
49252
49258
  TranslatorTranslationActionKey["HIDE"] = "hide";
49253
49259
  TranslatorTranslationActionKey["REMOVE"] = "remove";
49260
+ TranslatorTranslationActionKey["RETRY"] = "retry";
49254
49261
  return TranslatorTranslationActionKey;
49255
49262
  }({});
49256
49263
  /**
@@ -49288,6 +49295,7 @@ var TranslatorTranslationCommonKey = /* @__PURE__ */ function(TranslatorTranslat
49288
49295
  TranslatorTranslationCommonKey["REQUEST"] = "request";
49289
49296
  TranslatorTranslationCommonKey["DATA"] = "data";
49290
49297
  TranslatorTranslationCommonKey["BREADCRUMB"] = "breadcrumb";
49298
+ TranslatorTranslationCommonKey["LOAD_FAILED"] = "loadFailed";
49291
49299
  return TranslatorTranslationCommonKey;
49292
49300
  }({});
49293
49301
  //#endregion
@@ -49310,7 +49318,8 @@ var TranslatorTranslationActionEnglish = {
49310
49318
  [TranslatorTranslationActionKey.LOOKUP]: "Lookup",
49311
49319
  [TranslatorTranslationActionKey.SHOW]: "Show",
49312
49320
  [TranslatorTranslationActionKey.HIDE]: "Hide",
49313
- [TranslatorTranslationActionKey.REMOVE]: "Remove"
49321
+ [TranslatorTranslationActionKey.REMOVE]: "Remove",
49322
+ [TranslatorTranslationActionKey.RETRY]: "Retry"
49314
49323
  };
49315
49324
  //#endregion
49316
49325
  //#region ../../packages/i18n/src/catalogs/en/app.ts
@@ -49560,7 +49569,8 @@ var TranslatorTranslationCommonEnglish = {
49560
49569
  [TranslatorTranslationCommonKey.PROCESSING]: "Processing",
49561
49570
  [TranslatorTranslationCommonKey.REQUEST]: "Request",
49562
49571
  [TranslatorTranslationCommonKey.DATA]: "Data",
49563
- [TranslatorTranslationCommonKey.BREADCRUMB]: "Breadcrumb"
49572
+ [TranslatorTranslationCommonKey.BREADCRUMB]: "Breadcrumb",
49573
+ [TranslatorTranslationCommonKey.LOAD_FAILED]: "The content could not be loaded."
49564
49574
  };
49565
49575
  //#endregion
49566
49576
  //#region ../../packages/i18n/src/catalogs/en/entity.ts
@@ -49738,6 +49748,10 @@ var TranslatorTranslationFieldEnglish = {
49738
49748
  [TranslatorTranslationFieldKey.CLIENT_ID]: "Client ID",
49739
49749
  [TranslatorTranslationFieldKey.CLIENT_SECRET]: "Client secret",
49740
49750
  [TranslatorTranslationFieldKey.SCOPE]: "Scope",
49751
+ [TranslatorTranslationFieldKey.REQUIRED_AMR]: "Required amr claim (any of)",
49752
+ [TranslatorTranslationFieldKey.REQUIRED_ACR]: "Required acr value (any of)",
49753
+ [TranslatorTranslationFieldKey.REQUIRED_AMR_HINT]: "Accept the login when the provider token carries any one of these. Empty trusts the provider.",
49754
+ [TranslatorTranslationFieldKey.REQUIRED_ACR_HINT]: "Also sent as acr_values on the authorize request. Empty trusts the provider.",
49741
49755
  [TranslatorTranslationFieldKey.TOKEN]: "Token",
49742
49756
  [TranslatorTranslationFieldKey.USER_INFO]: "UserInfo",
49743
49757
  [TranslatorTranslationFieldKey.DISCOVERY]: "Discovery",
@@ -49794,7 +49808,8 @@ var TranslatorTranslationActionGerman = {
49794
49808
  [TranslatorTranslationActionKey.LOOKUP]: "Nachschlagen",
49795
49809
  [TranslatorTranslationActionKey.SHOW]: "Anzeigen",
49796
49810
  [TranslatorTranslationActionKey.HIDE]: "Verbergen",
49797
- [TranslatorTranslationActionKey.REMOVE]: "Entfernen"
49811
+ [TranslatorTranslationActionKey.REMOVE]: "Entfernen",
49812
+ [TranslatorTranslationActionKey.RETRY]: "Erneut versuchen"
49798
49813
  };
49799
49814
  //#endregion
49800
49815
  //#region ../../packages/i18n/src/catalogs/de/app.ts
@@ -50044,7 +50059,8 @@ var TranslatorTranslationCommonGerman = {
50044
50059
  [TranslatorTranslationCommonKey.PROCESSING]: "Wird verarbeitet",
50045
50060
  [TranslatorTranslationCommonKey.REQUEST]: "Anfrage",
50046
50061
  [TranslatorTranslationCommonKey.DATA]: "Daten",
50047
- [TranslatorTranslationCommonKey.BREADCRUMB]: "Navigationspfad"
50062
+ [TranslatorTranslationCommonKey.BREADCRUMB]: "Navigationspfad",
50063
+ [TranslatorTranslationCommonKey.LOAD_FAILED]: "Der Inhalt konnte nicht geladen werden."
50048
50064
  };
50049
50065
  //#endregion
50050
50066
  //#region ../../packages/i18n/src/catalogs/de/entity.ts
@@ -50222,6 +50238,10 @@ var TranslatorTranslationFieldGerman = {
50222
50238
  [TranslatorTranslationFieldKey.CLIENT_ID]: "Client-ID",
50223
50239
  [TranslatorTranslationFieldKey.CLIENT_SECRET]: "Client-Secret",
50224
50240
  [TranslatorTranslationFieldKey.SCOPE]: "Scope",
50241
+ [TranslatorTranslationFieldKey.REQUIRED_AMR]: "Erforderlicher amr-Claim (eines davon)",
50242
+ [TranslatorTranslationFieldKey.REQUIRED_ACR]: "Erforderlicher acr-Wert (einer davon)",
50243
+ [TranslatorTranslationFieldKey.REQUIRED_AMR_HINT]: "Login akzeptieren, wenn das Provider-Token einen dieser Werte trägt. Leer vertraut dem Provider.",
50244
+ [TranslatorTranslationFieldKey.REQUIRED_ACR_HINT]: "Wird auch als acr_values mit der Authorize-Anfrage gesendet. Leer vertraut dem Provider.",
50225
50245
  [TranslatorTranslationFieldKey.TOKEN]: "Token",
50226
50246
  [TranslatorTranslationFieldKey.USER_INFO]: "UserInfo",
50227
50247
  [TranslatorTranslationFieldKey.DISCOVERY]: "Discovery",
@@ -50278,7 +50298,8 @@ var TranslatorTranslationActionFrench = {
50278
50298
  [TranslatorTranslationActionKey.LOOKUP]: "Rechercher",
50279
50299
  [TranslatorTranslationActionKey.SHOW]: "Afficher",
50280
50300
  [TranslatorTranslationActionKey.HIDE]: "Masquer",
50281
- [TranslatorTranslationActionKey.REMOVE]: "Retirer"
50301
+ [TranslatorTranslationActionKey.REMOVE]: "Retirer",
50302
+ [TranslatorTranslationActionKey.RETRY]: "Réessayer"
50282
50303
  };
50283
50304
  //#endregion
50284
50305
  //#region ../../packages/i18n/src/catalogs/fr/app.ts
@@ -50528,7 +50549,8 @@ var TranslatorTranslationCommonFrench = {
50528
50549
  [TranslatorTranslationCommonKey.PROCESSING]: "Traitement en cours",
50529
50550
  [TranslatorTranslationCommonKey.REQUEST]: "Requête",
50530
50551
  [TranslatorTranslationCommonKey.DATA]: "Données",
50531
- [TranslatorTranslationCommonKey.BREADCRUMB]: "Fil d'Ariane"
50552
+ [TranslatorTranslationCommonKey.BREADCRUMB]: "Fil d'Ariane",
50553
+ [TranslatorTranslationCommonKey.LOAD_FAILED]: "Le contenu n'a pas pu être chargé."
50532
50554
  };
50533
50555
  //#endregion
50534
50556
  //#region ../../packages/i18n/src/catalogs/fr/entity.ts
@@ -50706,6 +50728,10 @@ var TranslatorTranslationFieldFrench = {
50706
50728
  [TranslatorTranslationFieldKey.CLIENT_ID]: "ID client",
50707
50729
  [TranslatorTranslationFieldKey.CLIENT_SECRET]: "Secret du client",
50708
50730
  [TranslatorTranslationFieldKey.SCOPE]: "Portée",
50731
+ [TranslatorTranslationFieldKey.REQUIRED_AMR]: "Claim amr requis (au choix)",
50732
+ [TranslatorTranslationFieldKey.REQUIRED_ACR]: "Valeur acr requise (au choix)",
50733
+ [TranslatorTranslationFieldKey.REQUIRED_AMR_HINT]: "Accepter la connexion si le jeton du fournisseur porte l'une de ces valeurs. Vide fait confiance au fournisseur.",
50734
+ [TranslatorTranslationFieldKey.REQUIRED_ACR_HINT]: "Également envoyé comme acr_values dans la requête d'autorisation. Vide fait confiance au fournisseur.",
50709
50735
  [TranslatorTranslationFieldKey.TOKEN]: "Jeton",
50710
50736
  [TranslatorTranslationFieldKey.USER_INFO]: "UserInfo",
50711
50737
  [TranslatorTranslationFieldKey.DISCOVERY]: "Découverte",
@@ -50762,7 +50788,8 @@ var TranslatorTranslationActionSpanish = {
50762
50788
  [TranslatorTranslationActionKey.LOOKUP]: "Buscar",
50763
50789
  [TranslatorTranslationActionKey.SHOW]: "Mostrar",
50764
50790
  [TranslatorTranslationActionKey.HIDE]: "Ocultar",
50765
- [TranslatorTranslationActionKey.REMOVE]: "Quitar"
50791
+ [TranslatorTranslationActionKey.REMOVE]: "Quitar",
50792
+ [TranslatorTranslationActionKey.RETRY]: "Reintentar"
50766
50793
  };
50767
50794
  //#endregion
50768
50795
  //#region ../../packages/i18n/src/catalogs/es/app.ts
@@ -51012,7 +51039,8 @@ var TranslatorTranslationCommonSpanish = {
51012
51039
  [TranslatorTranslationCommonKey.PROCESSING]: "Procesando",
51013
51040
  [TranslatorTranslationCommonKey.REQUEST]: "Solicitud",
51014
51041
  [TranslatorTranslationCommonKey.DATA]: "Datos",
51015
- [TranslatorTranslationCommonKey.BREADCRUMB]: "Ruta de navegación"
51042
+ [TranslatorTranslationCommonKey.BREADCRUMB]: "Ruta de navegación",
51043
+ [TranslatorTranslationCommonKey.LOAD_FAILED]: "No se pudo cargar el contenido."
51016
51044
  };
51017
51045
  //#endregion
51018
51046
  //#region ../../packages/i18n/src/catalogs/es/entity.ts
@@ -51190,6 +51218,10 @@ var TranslatorTranslationFieldSpanish = {
51190
51218
  [TranslatorTranslationFieldKey.CLIENT_ID]: "ID de cliente",
51191
51219
  [TranslatorTranslationFieldKey.CLIENT_SECRET]: "Secreto del cliente",
51192
51220
  [TranslatorTranslationFieldKey.SCOPE]: "Ámbito",
51221
+ [TranslatorTranslationFieldKey.REQUIRED_AMR]: "Claim amr requerido (cualquiera)",
51222
+ [TranslatorTranslationFieldKey.REQUIRED_ACR]: "Valor acr requerido (cualquiera)",
51223
+ [TranslatorTranslationFieldKey.REQUIRED_AMR_HINT]: "Aceptar el inicio de sesión cuando el token del proveedor lleve alguno de estos valores. Vacío confía en el proveedor.",
51224
+ [TranslatorTranslationFieldKey.REQUIRED_ACR_HINT]: "También se envía como acr_values en la solicitud de autorización. Vacío confía en el proveedor.",
51193
51225
  [TranslatorTranslationFieldKey.TOKEN]: "Token",
51194
51226
  [TranslatorTranslationFieldKey.USER_INFO]: "UserInfo",
51195
51227
  [TranslatorTranslationFieldKey.DISCOVERY]: "Descubrimiento",
@@ -58847,6 +58879,12 @@ var CookieName = /* @__PURE__ */ function(CookieName) {
58847
58879
  CookieName["ACCESS_TOKEN_EXPIRE_DATE"] = "access_token_expire_date";
58848
58880
  CookieName["REFRESH_TOKEN"] = "refresh_token";
58849
58881
  CookieName["ID_TOKEN"] = "id_token";
58882
+ /**
58883
+ * @deprecated No longer written by the client-web-kit store: the record is
58884
+ * unbounded (extra attributes are flattened onto the `/userinfo` response)
58885
+ * and is re-derived by `resolve()`. The name is kept so the store can sweep
58886
+ * copies written by earlier versions.
58887
+ */
58850
58888
  CookieName["USER"] = "user";
58851
58889
  CookieName["REALM"] = "realm";
58852
58890
  CookieName["REALM_MANAGEMENT"] = "realm_management";
@@ -89325,6 +89363,9 @@ var IdentityProviderAPI = class extends BaseAPI$1 {
89325
89363
  async createLinkRequest(id) {
89326
89364
  return (await this.client.post(`identity-providers/${id}/link-request`)).data;
89327
89365
  }
89366
+ async completeLogin(id) {
89367
+ return (await this.client.post(`identity-providers/${id}/login-complete`)).data;
89368
+ }
89328
89369
  async confirmLinkRequest(id, handle) {
89329
89370
  const payload = { handle };
89330
89371
  return (await this.client.post(`identity-providers/${id}/link-confirm`, payload)).data;
@@ -89704,7 +89745,8 @@ var UserAuthenticatorAPI = class extends BaseAPI$1 {
89704
89745
  return (await this.client.delete(`users/${userId}/authenticators/${id}`)).data;
89705
89746
  }
89706
89747
  async challenge(options) {
89707
- return (await this.client.get("authenticators/challenge", buildUserAuthenticatorChallengeRequestConfig(options))).data;
89748
+ const url = options && options.acrValues ? `authenticators/challenge?acrValues=${encodeURIComponent(options.acrValues)}` : "authenticators/challenge";
89749
+ return (await this.client.get(url, buildUserAuthenticatorChallengeRequestConfig(options))).data;
89708
89750
  }
89709
89751
  async sendChallenge(data, options) {
89710
89752
  return (await this.client.post("authenticators/challenge/send", data, buildUserAuthenticatorChallengeRequestConfig(options))).data;
@@ -93314,11 +93356,22 @@ function createStore(context) {
93314
93356
  idToken.value = input;
93315
93357
  context.dispatcher.emit(StoreDispatcherEventName.ID_TOKEN_UPDATED, input);
93316
93358
  };
93359
+ /**
93360
+ * The token's subject, narrowed to what is actually rendered: the id every
93361
+ * owner-scoped query filters on, and the name / display name the account
93362
+ * chip and the authorize page's "continue as" label read. A full `User` was
93363
+ * held here once, at the cost of a `/userinfo` round-trip per restore, and
93364
+ * no consumer ever read a field outside these three.
93365
+ */
93317
93366
  const user = (0, vue_exports.ref)(null);
93318
93367
  const userId = (0, vue_exports.computed)(() => user.value ? user.value.id : null);
93319
93368
  const setUser = (input) => {
93320
- user.value = input;
93321
- context.dispatcher.emit(StoreDispatcherEventName.USER_UPDATED, input);
93369
+ user.value = input ? {
93370
+ id: input.id,
93371
+ name: input.name,
93372
+ displayName: input.displayName
93373
+ } : null;
93374
+ context.dispatcher.emit(StoreDispatcherEventName.USER_UPDATED, user.value);
93322
93375
  };
93323
93376
  const sessionId = (0, vue_exports.ref)(null);
93324
93377
  const acr = (0, vue_exports.ref)(null);
@@ -93342,8 +93395,11 @@ function createStore(context) {
93342
93395
  const realmManagementId = (0, vue_exports.computed)(() => realmManagement.value ? realmManagement.value.id : realmId.value);
93343
93396
  const realmManagementName = (0, vue_exports.computed)(() => realmManagement.value ? realmManagement.value.name : realmName.value);
93344
93397
  const setRealmManagement = (input) => {
93345
- realmManagement.value = input;
93346
- context.dispatcher.emit(StoreDispatcherEventName.REALM_MANAGEMENT_UPDATED, input);
93398
+ realmManagement.value = input ? {
93399
+ id: input.id,
93400
+ name: input.name
93401
+ } : null;
93402
+ context.dispatcher.emit(StoreDispatcherEventName.REALM_MANAGEMENT_UPDATED, realmManagement.value);
93347
93403
  };
93348
93404
  const permissionProvider = new PermissionMemoryProvider();
93349
93405
  const permissionEvaluator = new PermissionEvaluator({
@@ -93386,21 +93442,49 @@ function createStore(context) {
93386
93442
  } catch {}
93387
93443
  return generation;
93388
93444
  };
93389
- const fetchTokenIntrospection = async (token) => client.token.introspect({ token }, { authorizationHeader: {
93390
- type: "Bearer",
93391
- token
93392
- } });
93393
- const fetchUserInfo = async (token) => client.userInfo.get(`Bearer ${token}`);
93394
93445
  /**
93395
- * Whether the currently held user IS the subject of the introspected
93396
- * token. A user that came from anywhere but this token (cookie
93397
- * hydration, a raw seed) is only as trustworthy as the pairing, and the
93398
- * two can drift apart (a sibling login on the same origin, an id from a
93399
- * previous provisioning run). Revalidation re-fetches on a mismatch
93400
- * instead of carrying the stale identity for the app's lifetime: it is
93401
- * what the UI renders and what its forms write against.
93446
+ * The `active` check is the point of the wrapper. The endpoint answers 200
93447
+ * with the full payload for a token it will not honour - revoked, or past
93448
+ * its expiry - so reading the claims alone commits a dead token as a live
93449
+ * session: identity set, permissions set, validated true, right up until
93450
+ * the next protected call 401s.
93451
+ *
93452
+ * Rejecting instead of returning a flag hands both staging sites the path
93453
+ * they already have for an introspection that failed outright: a stale
93454
+ * restore falls through to `refreshSession()`, and a fresh grant reverts
93455
+ * and revokes what it staged.
93402
93456
  */
93403
- const isTokenSubject = (value, introspection) => !!value && introspection.sub_kind === OAuth2SubKind.USER && introspection.sub === value.id;
93457
+ const fetchTokenIntrospection = async (token) => {
93458
+ const response = await client.token.introspect({ token }, { authorizationHeader: {
93459
+ type: "Bearer",
93460
+ token
93461
+ } });
93462
+ if (!response.active) throw new OAuth2Error("The access token is not active.");
93463
+ return response;
93464
+ };
93465
+ /**
93466
+ * The introspected token's subject, built from the response itself: the
93467
+ * introspection endpoint resolves the identity server-side and answers
93468
+ * with its OpenID claims (`name`, and `displayName` under `nickname`), so
93469
+ * a second `/userinfo` round-trip fetched a whole entity to restate three
93470
+ * fields the store already had in hand.
93471
+ *
93472
+ * Deriving it also settles the identity question the predecessor had to ask
93473
+ * separately: the held user cannot drift from the token, because it IS the
93474
+ * token's subject on every commit. A non-user subject yields null rather
93475
+ * than a failed lookup — `/userinfo` resolves `@me` through the user
93476
+ * service, which throws for a client actor and took the whole `resolve()`
93477
+ * down with it.
93478
+ */
93479
+ const buildUser = (introspection) => {
93480
+ if (introspection.sub_kind !== OAuth2SubKind.USER || !introspection.sub) return null;
93481
+ const { name, nickname } = introspection;
93482
+ return {
93483
+ id: introspection.sub,
93484
+ name: name || "",
93485
+ displayName: nickname || null
93486
+ };
93487
+ };
93404
93488
  const commitSession = (ctx) => {
93405
93489
  if (ctx.generation !== tokenGeneration.value) return false;
93406
93490
  if (ctx.grant) applyTokenGrantResponse(ctx.grant);
@@ -93414,7 +93498,8 @@ function createStore(context) {
93414
93498
  };
93415
93499
  if (!realmManagement.value) setRealmManagement(realm.value);
93416
93500
  }
93417
- if (ctx.user) setUser(ctx.user);
93501
+ const subject = buildUser(ctx.introspection);
93502
+ if (!user.value || user.value.id !== subject?.id) setUser(subject);
93418
93503
  if (ctx.introspection.permissions) permissionProvider.setMany(ctx.introspection.permissions.map((permission) => ({ permission: {
93419
93504
  name: permission.name,
93420
93505
  realmId: permission.realm_id,
@@ -93464,12 +93549,10 @@ function createStore(context) {
93464
93549
  const token = accessToken.value;
93465
93550
  if (!token) return;
93466
93551
  const introspection = await fetchTokenIntrospection(token);
93467
- const userInfo = isTokenSubject(user.value, introspection) ? void 0 : await fetchUserInfo(token);
93468
93552
  commitSession({
93469
93553
  generation,
93470
93554
  token,
93471
- introspection,
93472
- user: userInfo
93555
+ introspection
93473
93556
  });
93474
93557
  };
93475
93558
  const resolveInternal = async () => {
@@ -93496,13 +93579,11 @@ function createStore(context) {
93496
93579
  let committed = false;
93497
93580
  try {
93498
93581
  const introspection = await fetchTokenIntrospection(response.access_token);
93499
- const userInfo = await fetchUserInfo(response.access_token);
93500
93582
  committed = commitSession({
93501
93583
  generation,
93502
93584
  token: response.access_token,
93503
93585
  grant: response,
93504
93586
  introspection,
93505
- user: userInfo,
93506
93587
  origin
93507
93588
  });
93508
93589
  } finally {
@@ -94155,9 +94236,6 @@ function installStore(app, options = {}) {
94155
94236
  case CookieName.ID_TOKEN:
94156
94237
  if (!store.idToken) store.setIdToken(value);
94157
94238
  break;
94158
- case CookieName.USER:
94159
- if (!store.user) store.setUser(value);
94160
- break;
94161
94239
  case CookieName.REALM:
94162
94240
  if (!store.realm) store.setRealm(value);
94163
94241
  break;
@@ -94193,10 +94271,6 @@ function installStore(app, options = {}) {
94193
94271
  if (input) cookieSet(CookieName.ID_TOKEN, input, { path: cookiePath });
94194
94272
  else cookieUnset(CookieName.ID_TOKEN, { path: cookiePath });
94195
94273
  });
94196
- storeDispatcher.on(StoreDispatcherEventName.USER_UPDATED, (input) => {
94197
- if (input) cookieSet(CookieName.USER, input, { path: cookiePath });
94198
- else cookieUnset(CookieName.USER, { path: cookiePath });
94199
- });
94200
94274
  storeDispatcher.on(StoreDispatcherEventName.REALM_UPDATED, (input) => {
94201
94275
  if (input) cookieSet(CookieName.REALM, input, { path: cookiePath });
94202
94276
  else cookieUnset(CookieName.REALM, { path: cookiePath });
@@ -94206,6 +94280,26 @@ function installStore(app, options = {}) {
94206
94280
  else cookieUnset(CookieName.REALM_MANAGEMENT, { path: cookiePath });
94207
94281
  });
94208
94282
  dropShadowingCookies();
94283
+ /**
94284
+ * The user record is deliberately NOT persisted.
94285
+ *
94286
+ * Its value was the verbatim `/userinfo` body, and extra attributes are
94287
+ * flattened onto that response AFTER the query projection, so nothing
94288
+ * bounded its size: one operator-defined attribute is enough to push the
94289
+ * cookie past the 4096 byte limit, where the browser drops it silently.
94290
+ * It also carried the email and both names into the header of every
94291
+ * request on the origin, static assets included. The store now builds the
94292
+ * subject from the introspection response `resolve()` already awaits, so
94293
+ * there is nothing to persist and nothing to restore.
94294
+ *
94295
+ * Copies written by earlier versions are swept here rather than left to
94296
+ * expire: they carry no `maxAge`, so an open browser would keep sending one
94297
+ * until it closes. The sweep is conditional because the name is a bare
94298
+ * `user` on the pinned path — an unconditional delete would reach a
94299
+ * same-named cookie belonging to another app on the origin on every single
94300
+ * install, rather than once for a browser carrying the kit's own leftover.
94301
+ */
94302
+ if (cookieGet(CookieName.USER)) cookieUnset(CookieName.USER, { path: cookiePath });
94209
94303
  readCookies();
94210
94304
  provideStoreFactory(storeFactory, app);
94211
94305
  }
@@ -136783,15 +136877,21 @@ var AIdentityProviderOAuth2ClientFields_vue_vue_type_script_lang_default = (0, v
136783
136877
  const form = (0, vue_exports.reactive)({
136784
136878
  clientId: "",
136785
136879
  clientSecret: "",
136786
- scope: ""
136880
+ scope: "",
136881
+ requiredAmr: "",
136882
+ requiredAcr: ""
136787
136883
  });
136788
136884
  const secretShow = (0, vue_exports.ref)(false);
136789
136885
  const v = useValidup(new IdentityProviderOAuth2AttributesValidator({ pathsToInclude: [
136790
136886
  "clientId",
136791
136887
  "clientSecret",
136792
- "scope"
136888
+ "scope",
136889
+ "requiredAmr",
136890
+ "requiredAcr"
136793
136891
  ] }), form, { name: "client" });
136794
136892
  const scopeField = v.fields.at("scope");
136893
+ const requiredAmrField = v.fields.at("requiredAmr");
136894
+ const requiredAcrField = v.fields.at("requiredAcr");
136795
136895
  const scopes = (0, vue_exports.computed)(() => splitOAuth2Scope(scopeField.$model.value));
136796
136896
  function assign() {
136797
136897
  assignFormProperties(form, props.entity, { fields: v.fields });
@@ -136811,6 +136911,22 @@ var AIdentityProviderOAuth2ClientFields_vue_vue_type_script_lang_default = (0, v
136811
136911
  namespace: TranslatorTranslationNamespace.FIELD,
136812
136912
  key: TranslatorTranslationFieldKey.SCOPE
136813
136913
  },
136914
+ {
136915
+ namespace: TranslatorTranslationNamespace.FIELD,
136916
+ key: TranslatorTranslationFieldKey.REQUIRED_AMR
136917
+ },
136918
+ {
136919
+ namespace: TranslatorTranslationNamespace.FIELD,
136920
+ key: TranslatorTranslationFieldKey.REQUIRED_AMR_HINT
136921
+ },
136922
+ {
136923
+ namespace: TranslatorTranslationNamespace.FIELD,
136924
+ key: TranslatorTranslationFieldKey.REQUIRED_ACR
136925
+ },
136926
+ {
136927
+ namespace: TranslatorTranslationNamespace.FIELD,
136928
+ key: TranslatorTranslationFieldKey.REQUIRED_ACR_HINT
136929
+ },
136814
136930
  {
136815
136931
  namespace: TranslatorTranslationNamespace.ACTION,
136816
136932
  key: TranslatorTranslationActionKey.SHOW
@@ -136823,6 +136939,8 @@ var AIdentityProviderOAuth2ClientFields_vue_vue_type_script_lang_default = (0, v
136823
136939
  return {
136824
136940
  v,
136825
136941
  scopeField,
136942
+ requiredAmrField,
136943
+ requiredAcrField,
136826
136944
  scopes,
136827
136945
  secretShow,
136828
136946
  secretToggleLabel: (0, vue_exports.computed)(() => secretShow.value ? translations.hide : translations.show),
@@ -136982,6 +137100,82 @@ function _sfc_ssrRender$59(_ctx, _push, _parent, _attrs, $props, $setup, $data,
136982
137100
  }),
136983
137101
  _: 1
136984
137102
  }, _parent));
137103
+ _push((0, server_renderer_exports.ssrRenderComponent)(_component_IFieldValidation, { field: _ctx.requiredAmrField }, {
137104
+ default: (0, vue_exports.withCtx)(({ value }, _push, _parent, _scopeId) => {
137105
+ if (_push) _push((0, server_renderer_exports.ssrRenderComponent)(_component_VCFormGroup, { validation: value }, {
137106
+ label: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137107
+ if (_push) _push(`${(0, server_renderer_exports.ssrInterpolate)(_ctx.translations.requiredAmr)}`);
137108
+ else return [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAmr), 1)];
137109
+ }),
137110
+ hint: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137111
+ if (_push) _push(`${(0, server_renderer_exports.ssrInterpolate)(_ctx.translations.requiredAmrHint)}`);
137112
+ else return [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAmrHint), 1)];
137113
+ }),
137114
+ default: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137115
+ if (_push) _push((0, server_renderer_exports.ssrRenderComponent)(_component_VCFormInput, {
137116
+ modelValue: _ctx.requiredAmrField.$model.value,
137117
+ "onUpdate:modelValue": ($event) => _ctx.requiredAmrField.$model.value = $event,
137118
+ placeholder: "mfa, hwk"
137119
+ }, null, _parent, _scopeId));
137120
+ else return [(0, vue_exports.createVNode)(_component_VCFormInput, {
137121
+ modelValue: _ctx.requiredAmrField.$model.value,
137122
+ "onUpdate:modelValue": ($event) => _ctx.requiredAmrField.$model.value = $event,
137123
+ placeholder: "mfa, hwk"
137124
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])];
137125
+ }),
137126
+ _: 2
137127
+ }, _parent, _scopeId));
137128
+ else return [(0, vue_exports.createVNode)(_component_VCFormGroup, { validation: value }, {
137129
+ label: (0, vue_exports.withCtx)(() => [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAmr), 1)]),
137130
+ hint: (0, vue_exports.withCtx)(() => [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAmrHint), 1)]),
137131
+ default: (0, vue_exports.withCtx)(() => [(0, vue_exports.createVNode)(_component_VCFormInput, {
137132
+ modelValue: _ctx.requiredAmrField.$model.value,
137133
+ "onUpdate:modelValue": ($event) => _ctx.requiredAmrField.$model.value = $event,
137134
+ placeholder: "mfa, hwk"
137135
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])]),
137136
+ _: 2
137137
+ }, 1032, ["validation"])];
137138
+ }),
137139
+ _: 1
137140
+ }, _parent));
137141
+ _push((0, server_renderer_exports.ssrRenderComponent)(_component_IFieldValidation, { field: _ctx.requiredAcrField }, {
137142
+ default: (0, vue_exports.withCtx)(({ value }, _push, _parent, _scopeId) => {
137143
+ if (_push) _push((0, server_renderer_exports.ssrRenderComponent)(_component_VCFormGroup, { validation: value }, {
137144
+ label: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137145
+ if (_push) _push(`${(0, server_renderer_exports.ssrInterpolate)(_ctx.translations.requiredAcr)}`);
137146
+ else return [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAcr), 1)];
137147
+ }),
137148
+ hint: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137149
+ if (_push) _push(`${(0, server_renderer_exports.ssrInterpolate)(_ctx.translations.requiredAcrHint)}`);
137150
+ else return [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAcrHint), 1)];
137151
+ }),
137152
+ default: (0, vue_exports.withCtx)((_, _push, _parent, _scopeId) => {
137153
+ if (_push) _push((0, server_renderer_exports.ssrRenderComponent)(_component_VCFormInput, {
137154
+ modelValue: _ctx.requiredAcrField.$model.value,
137155
+ "onUpdate:modelValue": ($event) => _ctx.requiredAcrField.$model.value = $event,
137156
+ placeholder: "urn:mace:incommon:iap:silver"
137157
+ }, null, _parent, _scopeId));
137158
+ else return [(0, vue_exports.createVNode)(_component_VCFormInput, {
137159
+ modelValue: _ctx.requiredAcrField.$model.value,
137160
+ "onUpdate:modelValue": ($event) => _ctx.requiredAcrField.$model.value = $event,
137161
+ placeholder: "urn:mace:incommon:iap:silver"
137162
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])];
137163
+ }),
137164
+ _: 2
137165
+ }, _parent, _scopeId));
137166
+ else return [(0, vue_exports.createVNode)(_component_VCFormGroup, { validation: value }, {
137167
+ label: (0, vue_exports.withCtx)(() => [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAcr), 1)]),
137168
+ hint: (0, vue_exports.withCtx)(() => [(0, vue_exports.createTextVNode)((0, vue_exports.toDisplayString)(_ctx.translations.requiredAcrHint), 1)]),
137169
+ default: (0, vue_exports.withCtx)(() => [(0, vue_exports.createVNode)(_component_VCFormInput, {
137170
+ modelValue: _ctx.requiredAcrField.$model.value,
137171
+ "onUpdate:modelValue": ($event) => _ctx.requiredAcrField.$model.value = $event,
137172
+ placeholder: "urn:mace:incommon:iap:silver"
137173
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])]),
137174
+ _: 2
137175
+ }, 1032, ["validation"])];
137176
+ }),
137177
+ _: 1
137178
+ }, _parent));
136985
137179
  _push(`</div>`);
136986
137180
  }
136987
137181
  var _sfc_setup$60 = AIdentityProviderOAuth2ClientFields_vue_vue_type_script_lang_default.setup;
@@ -145084,13 +145278,23 @@ var Authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
145084
145278
  redirectUriVerified: {
145085
145279
  type: Boolean,
145086
145280
  default: false
145087
- }
145281
+ },
145282
+ /**
145283
+ * The one-time handle a federated callback put on this URL, for the
145284
+ * session it established (plan 094). It is redeemed AFTER mount, so
145285
+ * the resulting lastAuthOrigin change reaches the watch below and
145286
+ * the login counts as fresh: select_account then skips the chooser
145287
+ * (the account was chosen at the provider) and prompt=login does not
145288
+ * demand a second credential entry.
145289
+ */
145290
+ federatedLogin: { type: Object }
145088
145291
  },
145089
145292
  emits: ["redirect", "failed"],
145090
145293
  setup(props, { emit }) {
145091
145294
  const httpClient = injectHTTPClient();
145092
145295
  const store = injectStore();
145093
145296
  const { acr, loggedIn, lastAuthOrigin, realmId, user } = storeToRefs$1(store);
145297
+ const federatedLoginPending = (0, vue_exports.ref)(!!props.federatedLogin);
145094
145298
  const switchAccount = () => {
145095
145299
  store.logout();
145096
145300
  };
@@ -145115,13 +145319,14 @@ var Authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
145115
145319
  const accountConfirmed = (0, vue_exports.computed)(() => accountConfirmedLocal.value);
145116
145320
  const mfaStatus = (0, vue_exports.ref)(null);
145117
145321
  const mfaResolving = (0, vue_exports.ref)(false);
145322
+ const challengeOptions = () => ({ acrValues: props.codeRequest?.acr_values });
145118
145323
  const refreshMfaStatus = async () => {
145119
145324
  mfaResolving.value = true;
145120
145325
  try {
145121
- mfaStatus.value = await httpClient.userAuthenticator.challenge();
145326
+ mfaStatus.value = await httpClient.userAuthenticator.challenge(challengeOptions());
145122
145327
  } catch {
145123
145328
  try {
145124
- mfaStatus.value = await httpClient.userAuthenticator.challenge();
145329
+ mfaStatus.value = await httpClient.userAuthenticator.challenge(challengeOptions());
145125
145330
  } catch {
145126
145331
  mfaStatus.value = {
145127
145332
  required: false,
@@ -145133,6 +145338,29 @@ var Authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
145133
145338
  mfaResolving.value = false;
145134
145339
  }
145135
145340
  };
145341
+ /**
145342
+ * A redemption that failed is terminal for this render. The person
145343
+ * came back from an external provider, so continuing the ladder
145344
+ * against whatever session the cookies already hold would consent an
145345
+ * application into the WRONG account, silently for a builtIn client.
145346
+ */
145347
+ const federatedError = (0, vue_exports.ref)(null);
145348
+ (0, vue_exports.onMounted)(async () => {
145349
+ const federated = props.federatedLogin;
145350
+ if (!federated) return;
145351
+ try {
145352
+ const grant = await httpClient.identityProvider.completeLogin(federated.providerId);
145353
+ await store.loginWithTokenGrant(grant);
145354
+ } catch (e) {
145355
+ const ctx = extractErrorContext(e);
145356
+ const message = ctx.message ?? (e instanceof Error ? e.message : String(e));
145357
+ if (ctx.data?.reason !== "identityProviderLoginNotPending") store.logout();
145358
+ federatedError.value = message;
145359
+ emit("failed", message);
145360
+ } finally {
145361
+ federatedLoginPending.value = false;
145362
+ }
145363
+ });
145136
145364
  const consentStatus = (0, vue_exports.ref)(null);
145137
145365
  const requestedScopeTokens = (0, vue_exports.computed)(() => unwrapOAuth2Scope(props.codeRequest?.scope ?? []).filter((token) => token.length > 0));
145138
145366
  const refreshConsentStatus = async () => {
@@ -145231,6 +145459,11 @@ var Authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
145231
145459
  const redirect = silentRedirect(silentErrorCode.value);
145232
145460
  if (redirect) return redirect;
145233
145461
  }
145462
+ if (federatedLoginPending.value) return wrapChild((0, vue_exports.h)(AuthorizeText_default, { message: loadingText.value }));
145463
+ if (federatedError.value && loggedIn.value) return wrapChild((0, vue_exports.h)(AuthorizeText_default, {
145464
+ message: federatedError.value,
145465
+ isError: true
145466
+ }));
145234
145467
  const forceReauth = (isReauth || reauthRequired.value) && !accountConfirmed.value;
145235
145468
  if (!loggedIn.value || forceReauth) {
145236
145469
  if (isSilent) {
@@ -145247,6 +145480,10 @@ var Authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
145247
145480
  }),
145248
145481
  fallback: () => (0, vue_exports.h)(AuthorizeText_default, { message: loadingText.value })
145249
145482
  });
145483
+ if (federatedError.value) return wrapChild([(0, vue_exports.h)(AuthorizeText_default, {
145484
+ message: federatedError.value,
145485
+ isError: true
145486
+ }), loginNode]);
145250
145487
  return wrapChild(forceReauth ? [(0, vue_exports.h)(AuthorizeText_default, { message: reauthText.value }), loginNode] : loginNode);
145251
145488
  }
145252
145489
  if (!realmId.value) return wrapChild((0, vue_exports.h)(AuthorizeText_default, { message: loadingText.value }));
@@ -151392,6 +151629,13 @@ var authorize_vue_vue_type_script_lang_default = (0, vue_exports.defineComponent
151392
151629
  components: { AAuthorize: Authorize_default },
151393
151630
  setup() {
151394
151631
  const app = injectPayload();
151632
+ (0, vue_exports.onMounted)(() => {
151633
+ if (typeof window === "undefined") return;
151634
+ const url = new URL(window.location.href);
151635
+ if (!url.searchParams.has("provider")) return;
151636
+ url.searchParams.delete("provider");
151637
+ window.history.replaceState(window.history.state, "", url.href);
151638
+ });
151395
151639
  const withBasePath = useBasePath();
151396
151640
  const buildWorkflowLink = (path) => {
151397
151641
  const params = new URLSearchParams();
@@ -151428,6 +151672,7 @@ function _sfc_ssrRender$6(_ctx, _push, _parent, _attrs, $props, $setup, $data, $
151428
151672
  error: _ctx.data.error,
151429
151673
  realm: _ctx.data.realm,
151430
151674
  "redirect-uri-verified": _ctx.data.redirectUriVerified,
151675
+ "federated-login": _ctx.data.federatedLogin,
151431
151676
  "register-link": _ctx.registerLink,
151432
151677
  "password-forgot-link": _ctx.passwordForgotLink,
151433
151678
  onFailed: _ctx.handleFailed
@@ -151873,7 +152118,7 @@ function createWindowPayloadHTML(payload) {
151873
152118
  * against an older contract would otherwise fail per request on
151874
152119
  * `/authorize` rather than at boot with an actionable message.
151875
152120
  */
151876
- var CONTRACT_VERSION = 2;
152121
+ var CONTRACT_VERSION = 3;
151877
152122
  //#endregion
151878
152123
  //#region src/server.ts
151879
152124
  async function render(ctx) {