@synqauth/js 0.0.0 → 0.2.0

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as problemError, i as isSynqError, n as secureUrl, o as toSynqError, r as SynqError, t as createApiClient } from "./api-Cf9K1Rc9.js";
1
+ import { a as readSavedSignIn, c as problemError, i as newSignIn, l as toSynqError, n as secureUrl, o as SynqError, r as SignInFlow, s as isSynqError, t as createApiClient } from "./api-CeYAjkvX.js";
2
2
  import * as oauth from "oauth4webapi";
3
3
  //#region src/storage.ts
4
4
  /**
@@ -159,6 +159,7 @@ var Client = class {
159
159
  redirectUri: this.#options.redirectUri,
160
160
  resource: options.resource ?? this.#options.resource,
161
161
  maxAge: options.maxAge,
162
+ ...options.acr?.essential ? { acr: [...options.acr.values] } : {},
162
163
  createdAt: now()
163
164
  };
164
165
  const url = new URL(server.authorization_endpoint);
@@ -181,6 +182,15 @@ var Client = class {
181
182
  if (options.maxAge !== void 0) params.set("max_age", String(options.maxAge));
182
183
  if (options.loginHint) params.set("login_hint", options.loginHint);
183
184
  if (options.method) params.set("synq_method", options.method);
185
+ if (options.acr) {
186
+ if (options.acr.values.length === 0) throw new SynqError("invalid_options", "acr needs at least one value");
187
+ if (params.has("acr_values") || params.has("claims")) throw new SynqError("invalid_options", "acr_values and claims come from the acr option");
188
+ if (options.acr.essential) params.set("claims", JSON.stringify({ id_token: { acr: {
189
+ essential: true,
190
+ values: [...options.acr.values]
191
+ } } }));
192
+ else params.set("acr_values", options.acr.values.join(" "));
193
+ }
184
194
  await this.#transactions.set(TRANSACTION_PREFIX + transaction.state, JSON.stringify(transaction));
185
195
  if (!options.pushed) return url;
186
196
  if (!server.pushed_authorization_request_endpoint) throw new SynqError("discovery_failed", "The issuer takes no pushed authorization requests");
@@ -224,6 +234,7 @@ var Client = class {
224
234
  requireIdToken: true
225
235
  });
226
236
  const claims = oauth.getValidatedIdTokenClaims(result);
237
+ if (transaction.acr && !(claims?.acr && transaction.acr.includes(claims.acr))) throw new SynqError("unmet_authentication_requirements", "The sign-in did not reach the level this app requires");
227
238
  this.#generation += 1;
228
239
  await this.#clear();
229
240
  const session = await this.#save(result, claims);
@@ -271,6 +282,39 @@ var Client = class {
271
282
  this.#listeners.delete(listener);
272
283
  };
273
284
  }
285
+ startSignIn({ endUserIp, ...options } = {}) {
286
+ return new SignInFlow(this.#signInPort(endUserIp), newSignIn(options.scope ?? this.#options.scope ?? "openid", {
287
+ ...options,
288
+ resource: options.resource ?? this.#options.resource
289
+ }));
290
+ }
291
+ resumeSignIn(saved, options = {}) {
292
+ return new SignInFlow(this.#signInPort(options.endUserIp), readSavedSignIn(saved));
293
+ }
294
+ /** What a flow needs of this client: its issuer, its credentials, and keeping the session. */
295
+ #signInPort(endUserIp) {
296
+ return {
297
+ ...endUserIp ? { headers: { "synq-end-user-ip": endUserIp } } : {},
298
+ server: () => this.#discover(),
299
+ client: this.#client,
300
+ auth: this.#auth,
301
+ fetch: this.#options.fetch ?? globalThis.fetch.bind(globalThis),
302
+ finish: async (result, claims) => {
303
+ this.#generation += 1;
304
+ await this.#clear();
305
+ const session = await this.#save(result, claims, void 0, "app");
306
+ this.#emit({
307
+ type: "signed-in",
308
+ session
309
+ });
310
+ return session;
311
+ },
312
+ hosted: (options) => this.signIn({
313
+ ...options.method ? { method: options.method } : {},
314
+ ...options.loginHint ? { loginHint: options.loginHint } : {}
315
+ })
316
+ };
317
+ }
274
318
  async signOut(options = {}) {
275
319
  const stored = await this.#load();
276
320
  const refreshToken = await this.#readRefreshToken();
@@ -291,7 +335,7 @@ var Client = class {
291
335
  } catch (error) {
292
336
  if (attempt === 2) console.warn("@synqauth/js: revoking the refresh token at sign-out failed; it stays usable until it expires", toSynqError(error).code);
293
337
  }
294
- if (!server.end_session_endpoint) return null;
338
+ if (!server.end_session_endpoint || stored?.via === "app") return null;
295
339
  const url = new URL(server.end_session_endpoint);
296
340
  url.searchParams.set("client_id", this.#options.clientId);
297
341
  if (stored?.idToken) url.searchParams.set("id_token_hint", stored.idToken);
@@ -394,14 +438,15 @@ var Client = class {
394
438
  })();
395
439
  return this.#server;
396
440
  }
397
- async #save(result, claims, previous) {
441
+ async #save(result, claims, previous, via = previous?.via ?? "hosted") {
398
442
  const stored = {
399
443
  accessToken: result.access_token,
400
444
  tokenType: result.token_type,
401
445
  expiresAt: result.expires_in === void 0 ? void 0 : now() + result.expires_in,
402
446
  scope: result.scope ?? previous?.scope,
403
447
  idToken: result.id_token ?? previous?.idToken,
404
- claims
448
+ claims,
449
+ via
405
450
  };
406
451
  this.#session = stored;
407
452
  await this.#storage.set(SESSION_KEY, JSON.stringify(stored));
@@ -432,6 +477,7 @@ var Client = class {
432
477
  async #public(stored) {
433
478
  return {
434
479
  ...stored,
480
+ via: stored.via ?? "hosted",
435
481
  refreshable: await this.#readRefreshToken() !== void 0
436
482
  };
437
483
  }
@@ -534,6 +580,12 @@ function createAccountClient(options) {
534
580
  list: (opts) => list("/v1/consents", opts),
535
581
  withdraw: (consent, opts) => change("delete", "/v1/consents/{consent}", opts, { path: { consent } })
536
582
  },
583
+ apiKeys: {
584
+ apis: (opts) => list("/v1/apis", opts),
585
+ list: (opts) => list("/v1/api-keys", opts),
586
+ create: (input, opts) => change("post", "/v1/api-keys", opts, { body: input }),
587
+ revoke: (key, opts) => change("delete", "/v1/api-keys/{key}", opts, { path: { key } })
588
+ },
537
589
  activity: (opts) => list("/v1/sign-ins", opts),
538
590
  deletion: {
539
591
  schedule: (opts) => change("post", "/v1/deletion", opts),
@@ -542,6 +594,37 @@ function createAccountClient(options) {
542
594
  };
543
595
  }
544
596
  //#endregion
597
+ //#region src/generated/challenge.ts
598
+ /** The ways in an app's own screens sign people in with (`synq_method`). */
599
+ const CHALLENGE_METHODS = [
600
+ "password",
601
+ "email",
602
+ "passkey"
603
+ ];
604
+ /** What went wrong with a step (`synq_problem`): branch on it, never on the English. */
605
+ const CHALLENGE_PROBLEMS = [
606
+ "missing",
607
+ "invalid_email",
608
+ "wrong_credentials",
609
+ "email_taken",
610
+ "password_too_short",
611
+ "password_too_long",
612
+ "password_guessable",
613
+ "password_breached",
614
+ "wrong_code",
615
+ "code_expired",
616
+ "code_used_up",
617
+ "address_suppressed",
618
+ "team_only",
619
+ "send_failed",
620
+ "passkey_failed",
621
+ "passkey_unknown",
622
+ "passkey_expired",
623
+ "slow_down",
624
+ "locked",
625
+ "limited"
626
+ ];
627
+ //#endregion
545
628
  //#region src/generated/errors.ts
546
629
  /** Each `code` a Synq API's problem documents carry, with its HTTP status. */
547
630
  const ERROR_CODES = {
@@ -597,9 +680,11 @@ const ERROR_CODES = {
597
680
  upload_expired: 410,
598
681
  file_rejected: 422,
599
682
  upload_limit_reached: 409,
683
+ api_keys_not_allowed: 403,
684
+ api_key_limit_reached: 409,
600
685
  rate_limited: 429,
601
686
  internal_error: 500,
602
687
  service_unavailable: 503
603
688
  };
604
689
  //#endregion
605
- export { ERROR_CODES, SynqError, accountApiUrl, browserLocalStorage, browserSessionStorage, createAccountClient, createSynqClient, isSynqError, linkSignIn, memoryStorage, problemError };
690
+ export { CHALLENGE_METHODS, CHALLENGE_PROBLEMS, ERROR_CODES, SignInFlow, SynqError, accountApiUrl, browserLocalStorage, browserSessionStorage, createAccountClient, createSynqClient, isSynqError, linkSignIn, memoryStorage, problemError };