@faable/auth-sdk 2.8.21 → 2.8.23

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.
@@ -108,6 +108,7 @@ export type UserAppCreate = components["schemas"]["UserAppCreate"];
108
108
  export type UserAppMetadata = components["schemas"]["UserAppMetadata"];
109
109
  export type UserAppMetadataUpdate = components["schemas"]["UserAppMetadataUpdate"];
110
110
  export type UserCreate = components["schemas"]["UserCreate"];
111
+ export type UserIpInsight = components["schemas"]["UserIpInsight"];
111
112
  export type UserMetadata = components["schemas"]["UserMetadata"];
112
113
  export type UserUpdate = components["schemas"]["UserUpdate"];
113
114
  export type UserUserCreate = components["schemas"]["UserUserCreate"];
@@ -463,22 +463,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
463
463
  slug: string;
464
464
  logo_src?: string | null | undefined;
465
465
  icon_src?: string | null | undefined;
466
- callback_hostnames: string[]; /**
467
- * `POST /user/import` — operationId: `user/import`
468
- *
469
- * Import users, with their password hashes
470
- *
471
- * Creates up to 500 users per request, for migrations from another provider. Each row can carry a `password_hash` exported from that provider (bcrypt, scrypt, Firebase scrypt, PBKDF2, argon2): the user signs in with their existing password and the hash is upgraded to argon2id on that first login. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused per row with `weak_password_hash`. Rows are matched by email, so the same file can be sent twice: existing users are `skipped` unless `update_existing`. A bad row never fails the batch: each row gets its own `status` and `error`. Imported users get none of the built-in new-user emails. The response never contains a hash, and neither does the audit log.
472
- *
473
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
474
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
475
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
476
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
477
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
478
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
479
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
480
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
481
- */
466
+ callback_hostnames: string[];
482
467
  token_signature: string;
483
468
  token_signing_alg: "RS256";
484
469
  default_connection?: unknown;
@@ -548,22 +533,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
548
533
  slug: string;
549
534
  logo_src?: string | null | undefined;
550
535
  icon_src?: string | null | undefined;
551
- callback_hostnames: string[]; /**
552
- * `POST /user/import` — operationId: `user/import`
553
- *
554
- * Import users, with their password hashes
555
- *
556
- * Creates up to 500 users per request, for migrations from another provider. Each row can carry a `password_hash` exported from that provider (bcrypt, scrypt, Firebase scrypt, PBKDF2, argon2): the user signs in with their existing password and the hash is upgraded to argon2id on that first login. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused per row with `weak_password_hash`. Rows are matched by email, so the same file can be sent twice: existing users are `skipped` unless `update_existing`. A bad row never fails the batch: each row gets its own `status` and `error`. Imported users get none of the built-in new-user emails. The response never contains a hash, and neither does the audit log.
557
- *
558
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
559
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
560
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
561
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
562
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
563
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
564
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
565
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
566
- */
536
+ callback_hostnames: string[];
567
537
  token_signature: string;
568
538
  token_signing_alg: "RS256";
569
539
  default_connection?: unknown;
@@ -636,22 +606,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
636
606
  slug: string;
637
607
  logo_src?: string | null | undefined;
638
608
  icon_src?: string | null | undefined;
639
- callback_hostnames: string[]; /**
640
- * `POST /user/import` — operationId: `user/import`
641
- *
642
- * Import users, with their password hashes
643
- *
644
- * Creates up to 500 users per request, for migrations from another provider. Each row can carry a `password_hash` exported from that provider (bcrypt, scrypt, Firebase scrypt, PBKDF2, argon2): the user signs in with their existing password and the hash is upgraded to argon2id on that first login. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused per row with `weak_password_hash`. Rows are matched by email, so the same file can be sent twice: existing users are `skipped` unless `update_existing`. A bad row never fails the batch: each row gets its own `status` and `error`. Imported users get none of the built-in new-user emails. The response never contains a hash, and neither does the audit log.
645
- *
646
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
647
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
648
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
649
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
650
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
651
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
652
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
653
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
654
- */
609
+ callback_hostnames: string[];
655
610
  token_signature: string;
656
611
  token_signing_alg: "RS256";
657
612
  default_connection?: unknown;
@@ -5769,6 +5724,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5769
5724
  status: "active" | "revoked";
5770
5725
  ip?: string | undefined;
5771
5726
  user_agent?: string | undefined;
5727
+ language?: string | undefined;
5772
5728
  device_name?: string | undefined;
5773
5729
  client?: unknown;
5774
5730
  connection?: unknown;
@@ -5809,6 +5765,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5809
5765
  status: "active" | "revoked";
5810
5766
  ip?: string | undefined;
5811
5767
  user_agent?: string | undefined;
5768
+ language?: string | undefined;
5812
5769
  device_name?: string | undefined;
5813
5770
  client?: unknown;
5814
5771
  connection?: unknown;
@@ -5831,6 +5788,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5831
5788
  status: "active" | "revoked";
5832
5789
  ip?: string | undefined;
5833
5790
  user_agent?: string | undefined;
5791
+ language?: string | undefined;
5834
5792
  device_name?: string | undefined;
5835
5793
  client?: unknown;
5836
5794
  connection?: unknown;
@@ -5856,6 +5814,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5856
5814
  status: "active" | "revoked";
5857
5815
  ip?: string | undefined;
5858
5816
  user_agent?: string | undefined;
5817
+ language?: string | undefined;
5859
5818
  device_name?: string | undefined;
5860
5819
  client?: unknown;
5861
5820
  connection?: unknown;
@@ -5897,6 +5856,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5897
5856
  status: "active" | "revoked";
5898
5857
  ip?: string | undefined;
5899
5858
  user_agent?: string | undefined;
5859
+ language?: string | undefined;
5900
5860
  device_name?: string | undefined;
5901
5861
  client?: unknown;
5902
5862
  connection?: unknown;
@@ -6919,6 +6879,52 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
6919
6879
  } | undefined;
6920
6880
  }[];
6921
6881
  }>;
6882
+ /**
6883
+ * `GET /user/{user_id}/ip` — operationId: `user/ip`
6884
+ *
6885
+ * Insight into a user's last IP
6886
+ *
6887
+ * Location and network (country, ASN), reputation signals and usage of the user's `last_ip`. Empty when the user has never logged in.
6888
+ *
6889
+ * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
6890
+ * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
6891
+ * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
6892
+ * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
6893
+ * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
6894
+ * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
6895
+ * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
6896
+ * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
6897
+ */
6898
+ userIp(user_id: string): Promise<{
6899
+ ip?: string | undefined;
6900
+ geo?: {
6901
+ country_code?: string | undefined;
6902
+ country_name?: string | undefined;
6903
+ continent?: string | undefined;
6904
+ asn?: number | undefined;
6905
+ as_org?: string | undefined;
6906
+ source: {
6907
+ name: string;
6908
+ url: string;
6909
+ license: string;
6910
+ edition: string;
6911
+ };
6912
+ } | undefined;
6913
+ signals?: {
6914
+ datacenter: boolean;
6915
+ apple_private_relay: boolean;
6916
+ active_blocks: number;
6917
+ network_active_blocks: number;
6918
+ suspended_denies: number;
6919
+ network_suspended_denies: number;
6920
+ } | undefined;
6921
+ activity?: {
6922
+ logins: number;
6923
+ first_login_at?: string | undefined;
6924
+ active_sessions: number;
6925
+ other_users: number;
6926
+ } | undefined;
6927
+ }>;
6922
6928
  /**
6923
6929
  * `GET /user` — operationId: `user/list`
6924
6930
  *
@@ -2623,6 +2623,26 @@ export class GeneratedFaableAuthApi extends FaableApi {
2623
2623
  userImport(data) {
2624
2624
  return this.fetcher.post(`/user/import`, data);
2625
2625
  }
2626
+ /**
2627
+ * `GET /user/{user_id}/ip` — operationId: `user/ip`
2628
+ *
2629
+ * Insight into a user's last IP
2630
+ *
2631
+ * Location and network (country, ASN), reputation signals and usage of the user's `last_ip`. Empty when the user has never logged in.
2632
+ *
2633
+ * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
2634
+ * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
2635
+ * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
2636
+ * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
2637
+ * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
2638
+ * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
2639
+ * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
2640
+ * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
2641
+ */
2642
+ userIp(user_id) {
2643
+ requireId("user_id", user_id);
2644
+ return this.fetcher.get(`/user/${user_id}/ip`);
2645
+ }
2626
2646
  /**
2627
2647
  * `GET /user` — operationId: `user/list`
2628
2648
  *
@@ -880,6 +880,26 @@ export interface paths {
880
880
  patch?: never;
881
881
  trace?: never;
882
882
  };
883
+ "/user/{user_id}/ip": {
884
+ parameters: {
885
+ query?: never;
886
+ header?: never;
887
+ path?: never;
888
+ cookie?: never;
889
+ };
890
+ /**
891
+ * Insight into a user's last IP
892
+ * @description Location and network (country, ASN), reputation signals and usage of the user's `last_ip`. Empty when the user has never logged in.
893
+ */
894
+ get: operations["user/ip"];
895
+ put?: never;
896
+ post?: never;
897
+ delete?: never;
898
+ options?: never;
899
+ head?: never;
900
+ patch?: never;
901
+ trace?: never;
902
+ };
883
903
  "/user/import": {
884
904
  parameters: {
885
905
  query?: never;
@@ -6497,6 +6517,42 @@ export interface components {
6497
6517
  last_used_at?: string;
6498
6518
  remaining?: number;
6499
6519
  };
6520
+ UserIpInsight: {
6521
+ ip?: string;
6522
+ geo?: {
6523
+ country_code?: string;
6524
+ country_name?: string;
6525
+ continent?: string;
6526
+ asn?: number;
6527
+ as_org?: string;
6528
+ /** @description Where the location comes from. The data is CC BY 4.0: anything that shows it must credit this source. */
6529
+ source: {
6530
+ name: string;
6531
+ url: string;
6532
+ license: string;
6533
+ edition: string;
6534
+ };
6535
+ };
6536
+ /** @description The same values a tenant Action reads as `event.request.ip_*` for a login from this IP now. */
6537
+ signals?: {
6538
+ datacenter: boolean;
6539
+ apple_private_relay: boolean;
6540
+ active_blocks: number;
6541
+ network_active_blocks: number;
6542
+ suspended_denies: number;
6543
+ network_suspended_denies: number;
6544
+ };
6545
+ activity?: {
6546
+ /** @description This user's logins from this IP in the last year. */
6547
+ logins: number;
6548
+ /** Format: date-time */
6549
+ first_login_at?: string;
6550
+ /** @description This user's active sessions last seen from this IP. */
6551
+ active_sessions: number;
6552
+ /** @description Other users of this tenant whose last login came from this IP. */
6553
+ other_users: number;
6554
+ };
6555
+ };
6500
6556
  /** @description Identity */
6501
6557
  Identity: {
6502
6558
  /** @description Identity ID */
@@ -7992,6 +8048,8 @@ export interface components {
7992
8048
  ip?: string;
7993
8049
  /** @description Raw `User-Agent` header at the last authentication. */
7994
8050
  user_agent?: string;
8051
+ /** @description First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`. */
8052
+ language?: string;
7995
8053
  /** @description The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one. */
7996
8054
  device_name?: string;
7997
8055
  client?: components["schemas"]["Client"] | string | unknown;
@@ -16535,6 +16593,106 @@ export interface operations {
16535
16593
  };
16536
16594
  };
16537
16595
  };
16596
+ "user/ip": {
16597
+ parameters: {
16598
+ query?: never;
16599
+ header?: never;
16600
+ path: {
16601
+ user_id: string;
16602
+ };
16603
+ cookie?: never;
16604
+ };
16605
+ requestBody?: never;
16606
+ responses: {
16607
+ /** @description Default Response */
16608
+ 200: {
16609
+ headers: {
16610
+ [name: string]: unknown;
16611
+ };
16612
+ content: {
16613
+ "application/json": components["schemas"]["UserIpInsight"];
16614
+ };
16615
+ };
16616
+ /** @description `validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field. */
16617
+ 400: {
16618
+ headers: {
16619
+ [name: string]: unknown;
16620
+ };
16621
+ content: {
16622
+ "application/json": components["schemas"]["ErrorResponse"] & {
16623
+ /** @enum {string} */
16624
+ error_code?: "validation_error";
16625
+ };
16626
+ };
16627
+ };
16628
+ /** @description `unauthorized` — The request carries no valid credentials. */
16629
+ 401: {
16630
+ headers: {
16631
+ [name: string]: unknown;
16632
+ };
16633
+ content: {
16634
+ "application/json": components["schemas"]["ErrorResponse"] & {
16635
+ /** @enum {string} */
16636
+ error_code?: "unauthorized";
16637
+ };
16638
+ };
16639
+ };
16640
+ /**
16641
+ * @description `forbidden` — The credentials are valid but do not allow this operation.
16642
+ *
16643
+ * `insufficient_scope` — The token lacks a scope this operation requires. `message` names it.
16644
+ *
16645
+ * `user_suspended` — The user is suspended and cannot sign in or be modified.
16646
+ */
16647
+ 403: {
16648
+ headers: {
16649
+ [name: string]: unknown;
16650
+ };
16651
+ content: {
16652
+ "application/json": components["schemas"]["ErrorResponse"] & {
16653
+ /** @enum {string} */
16654
+ error_code?: "forbidden" | "insufficient_scope" | "user_suspended";
16655
+ };
16656
+ };
16657
+ };
16658
+ /** @description `account_not_found` — No Auth Account matches the request (domain, header or token). */
16659
+ 404: {
16660
+ headers: {
16661
+ [name: string]: unknown;
16662
+ };
16663
+ content: {
16664
+ "application/json": components["schemas"]["ErrorResponse"] & {
16665
+ /** @enum {string} */
16666
+ error_code?: "account_not_found";
16667
+ };
16668
+ };
16669
+ };
16670
+ /** @description `too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header. */
16671
+ 429: {
16672
+ headers: {
16673
+ [name: string]: unknown;
16674
+ };
16675
+ content: {
16676
+ "application/json": components["schemas"]["ErrorResponse"] & {
16677
+ /** @enum {string} */
16678
+ error_code?: "too_many_requests";
16679
+ };
16680
+ };
16681
+ };
16682
+ /** @description `internal_error` — Unexpected server error. Retry later; the request id is logged. */
16683
+ 500: {
16684
+ headers: {
16685
+ [name: string]: unknown;
16686
+ };
16687
+ content: {
16688
+ "application/json": components["schemas"]["ErrorResponse"] & {
16689
+ /** @enum {string} */
16690
+ error_code?: "internal_error";
16691
+ };
16692
+ };
16693
+ };
16694
+ };
16695
+ };
16538
16696
  "user/import": {
16539
16697
  parameters: {
16540
16698
  query?: never;
@@ -30595,6 +30753,8 @@ export interface operations {
30595
30753
  ip?: string;
30596
30754
  /** @description Raw `User-Agent` header at the last authentication. */
30597
30755
  user_agent?: string;
30756
+ /** @description First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`. */
30757
+ language?: string;
30598
30758
  /** @description The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one. */
30599
30759
  device_name?: string;
30600
30760
  client?: components["schemas"]["Client"] | string | unknown;
@@ -30750,6 +30910,8 @@ export interface operations {
30750
30910
  ip?: string;
30751
30911
  /** @description Raw `User-Agent` header at the last authentication. */
30752
30912
  user_agent?: string;
30913
+ /** @description First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`. */
30914
+ language?: string;
30753
30915
  /** @description The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one. */
30754
30916
  device_name?: string;
30755
30917
  client?: components["schemas"]["Client"] | string | unknown;
package/dist/version.js CHANGED
@@ -9,13 +9,13 @@
9
9
  // login pages could not import the SDK at all. Same pattern as auth-js.
10
10
  //
11
11
  // The sentinels MUST stay byte-identical to the `from` values in `.releaserc`.
12
- export const version = "2.8.21";
12
+ export const version = "2.8.23";
13
13
  // Short git SHA of the released commit. The version dates a build; this names
14
14
  // the exact tree, so a canonical log line or an audit entry leads straight to
15
15
  // `git show <sha>`. Deliberately NOT hex: an unreleased build (dev, a local
16
16
  // link) cannot be mistaken for a real commit — auth only records values that
17
17
  // look like a SHA, and this one never will.
18
- export const commit = "7e170f3";
18
+ export const commit = "80d1e2f";
19
19
  // What this SDK writes in `x-faable-client`. Exported for a consumer that
20
20
  // builds a strategy on its own (outside `FaableAuthApi`) and still wants the
21
21
  // token request attributed — a bare `authClientCredentials` stamps only what
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faable/auth-sdk",
3
- "version": "2.8.21",
3
+ "version": "2.8.23",
4
4
  "author": "Marc Pomar <marc@faable.com>",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/spec/openapi.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "info": {
4
4
  "title": "@faablecloud/auth",
5
5
  "description": "Auth Platform made by Faable. Manage Users and Roles",
6
- "version": "2.87.1",
6
+ "version": "2.89.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -3704,6 +3704,119 @@
3704
3704
  }
3705
3705
  }
3706
3706
  },
3707
+ "UserIpInsight": {
3708
+ "type": "object",
3709
+ "properties": {
3710
+ "ip": {
3711
+ "type": "string"
3712
+ },
3713
+ "geo": {
3714
+ "type": "object",
3715
+ "required": [
3716
+ "source"
3717
+ ],
3718
+ "properties": {
3719
+ "country_code": {
3720
+ "type": "string"
3721
+ },
3722
+ "country_name": {
3723
+ "type": "string"
3724
+ },
3725
+ "continent": {
3726
+ "type": "string"
3727
+ },
3728
+ "asn": {
3729
+ "type": "integer"
3730
+ },
3731
+ "as_org": {
3732
+ "type": "string"
3733
+ },
3734
+ "source": {
3735
+ "type": "object",
3736
+ "required": [
3737
+ "name",
3738
+ "url",
3739
+ "license",
3740
+ "edition"
3741
+ ],
3742
+ "properties": {
3743
+ "name": {
3744
+ "type": "string"
3745
+ },
3746
+ "url": {
3747
+ "type": "string"
3748
+ },
3749
+ "license": {
3750
+ "type": "string"
3751
+ },
3752
+ "edition": {
3753
+ "type": "string"
3754
+ }
3755
+ },
3756
+ "description": "Where the location comes from. The data is CC BY 4.0: anything that shows it must credit this source."
3757
+ }
3758
+ }
3759
+ },
3760
+ "signals": {
3761
+ "type": "object",
3762
+ "required": [
3763
+ "datacenter",
3764
+ "apple_private_relay",
3765
+ "active_blocks",
3766
+ "network_active_blocks",
3767
+ "suspended_denies",
3768
+ "network_suspended_denies"
3769
+ ],
3770
+ "properties": {
3771
+ "datacenter": {
3772
+ "type": "boolean"
3773
+ },
3774
+ "apple_private_relay": {
3775
+ "type": "boolean"
3776
+ },
3777
+ "active_blocks": {
3778
+ "type": "integer"
3779
+ },
3780
+ "network_active_blocks": {
3781
+ "type": "integer"
3782
+ },
3783
+ "suspended_denies": {
3784
+ "type": "integer"
3785
+ },
3786
+ "network_suspended_denies": {
3787
+ "type": "integer"
3788
+ }
3789
+ },
3790
+ "description": "The same values a tenant Action reads as `event.request.ip_*` for a login from this IP now."
3791
+ },
3792
+ "activity": {
3793
+ "type": "object",
3794
+ "required": [
3795
+ "logins",
3796
+ "active_sessions",
3797
+ "other_users"
3798
+ ],
3799
+ "properties": {
3800
+ "logins": {
3801
+ "type": "integer",
3802
+ "description": "This user's logins from this IP in the last year."
3803
+ },
3804
+ "first_login_at": {
3805
+ "type": "string",
3806
+ "format": "date-time"
3807
+ },
3808
+ "active_sessions": {
3809
+ "type": "integer",
3810
+ "description": "This user's active sessions last seen from this IP."
3811
+ },
3812
+ "other_users": {
3813
+ "type": "integer",
3814
+ "description": "Other users of this tenant whose last login came from this IP."
3815
+ }
3816
+ }
3817
+ }
3818
+ }
3819
+ },
3707
3820
  "Identity": {
3708
3821
  "type": "object",
3709
3822
  "required": [
@@ -7931,6 +8044,10 @@
7931
8044
  "type": "string",
7932
8045
  "description": "Raw `User-Agent` header at the last authentication."
7933
8046
  },
8047
+ "language": {
8048
+ "type": "string",
8049
+ "description": "First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`."
8050
+ },
7934
8051
  "device_name": {
7935
8052
  "type": "string",
7936
8053
  "description": "The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."
@@ -30921,6 +31038,195 @@
30921
31038
  }
30922
31039
  }
30923
31040
  },
31041
+ "/user/{user_id}/ip": {
31042
+ "get": {
31043
+ "operationId": "user/ip",
31044
+ "summary": "Insight into a user's last IP",
31045
+ "tags": [
31046
+ "user"
31047
+ ],
31048
+ "description": "Location and network (country, ASN), reputation signals and usage of the user's `last_ip`. Empty when the user has never logged in.",
31049
+ "parameters": [
31050
+ {
31051
+ "schema": {
31052
+ "type": "string"
31053
+ },
31054
+ "in": "path",
31055
+ "name": "user_id",
31056
+ "required": true
31057
+ }
31058
+ ],
31059
+ "security": [
31060
+ {
31061
+ "bearerAuth": []
31062
+ }
31063
+ ],
31064
+ "responses": {
31065
+ "200": {
31066
+ "description": "Default Response",
31067
+ "content": {
31068
+ "application/json": {
31069
+ "schema": {
31070
+ "$ref": "#/components/schemas/UserIpInsight"
31071
+ }
31072
+ }
31073
+ }
31074
+ },
31075
+ "400": {
31076
+ "description": "`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
31077
+ "content": {
31078
+ "application/json": {
31079
+ "schema": {
31080
+ "allOf": [
31081
+ {
31082
+ "$ref": "#/components/schemas/ErrorResponse"
31083
+ },
31084
+ {
31085
+ "type": "object",
31086
+ "properties": {
31087
+ "error_code": {
31088
+ "type": "string",
31089
+ "enum": [
31090
+ "validation_error"
31091
+ ]
31092
+ }
31093
+ }
31094
+ }
31095
+ ]
31096
+ }
31097
+ }
31098
+ }
31099
+ },
31100
+ "401": {
31101
+ "description": "`unauthorized` — The request carries no valid credentials.",
31102
+ "content": {
31103
+ "application/json": {
31104
+ "schema": {
31105
+ "allOf": [
31106
+ {
31107
+ "$ref": "#/components/schemas/ErrorResponse"
31108
+ },
31109
+ {
31110
+ "type": "object",
31111
+ "properties": {
31112
+ "error_code": {
31113
+ "type": "string",
31114
+ "enum": [
31115
+ "unauthorized"
31116
+ ]
31117
+ }
31118
+ }
31119
+ }
31120
+ ]
31121
+ }
31122
+ }
31123
+ }
31124
+ },
31125
+ "403": {
31126
+ "description": "`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.",
31127
+ "content": {
31128
+ "application/json": {
31129
+ "schema": {
31130
+ "allOf": [
31131
+ {
31132
+ "$ref": "#/components/schemas/ErrorResponse"
31133
+ },
31134
+ {
31135
+ "type": "object",
31136
+ "properties": {
31137
+ "error_code": {
31138
+ "type": "string",
31139
+ "enum": [
31140
+ "forbidden",
31141
+ "insufficient_scope",
31142
+ "user_suspended"
31143
+ ]
31144
+ }
31145
+ }
31146
+ }
31147
+ ]
31148
+ }
31149
+ }
31150
+ }
31151
+ },
31152
+ "404": {
31153
+ "description": "`account_not_found` — No Auth Account matches the request (domain, header or token).",
31154
+ "content": {
31155
+ "application/json": {
31156
+ "schema": {
31157
+ "allOf": [
31158
+ {
31159
+ "$ref": "#/components/schemas/ErrorResponse"
31160
+ },
31161
+ {
31162
+ "type": "object",
31163
+ "properties": {
31164
+ "error_code": {
31165
+ "type": "string",
31166
+ "enum": [
31167
+ "account_not_found"
31168
+ ]
31169
+ }
31170
+ }
31171
+ }
31172
+ ]
31173
+ }
31174
+ }
31175
+ }
31176
+ },
31177
+ "429": {
31178
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
31179
+ "content": {
31180
+ "application/json": {
31181
+ "schema": {
31182
+ "allOf": [
31183
+ {
31184
+ "$ref": "#/components/schemas/ErrorResponse"
31185
+ },
31186
+ {
31187
+ "type": "object",
31188
+ "properties": {
31189
+ "error_code": {
31190
+ "type": "string",
31191
+ "enum": [
31192
+ "too_many_requests"
31193
+ ]
31194
+ }
31195
+ }
31196
+ }
31197
+ ]
31198
+ }
31199
+ }
31200
+ }
31201
+ },
31202
+ "500": {
31203
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
31204
+ "content": {
31205
+ "application/json": {
31206
+ "schema": {
31207
+ "allOf": [
31208
+ {
31209
+ "$ref": "#/components/schemas/ErrorResponse"
31210
+ },
31211
+ {
31212
+ "type": "object",
31213
+ "properties": {
31214
+ "error_code": {
31215
+ "type": "string",
31216
+ "enum": [
31217
+ "internal_error"
31218
+ ]
31219
+ }
31220
+ }
31221
+ }
31222
+ ]
31223
+ }
31224
+ }
31225
+ }
31226
+ }
31227
+ }
31228
+ }
31229
+ },
30924
31230
  "/user/import": {
30925
31231
  "post": {
30926
31232
  "operationId": "user/import",
@@ -62629,6 +62935,10 @@
62629
62935
  "type": "string",
62630
62936
  "description": "Raw `User-Agent` header at the last authentication."
62631
62937
  },
62938
+ "language": {
62939
+ "type": "string",
62940
+ "description": "First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`."
62941
+ },
62632
62942
  "device_name": {
62633
62943
  "type": "string",
62634
62944
  "description": "The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."
@@ -62970,6 +63280,10 @@
62970
63280
  "type": "string",
62971
63281
  "description": "Raw `User-Agent` header at the last authentication."
62972
63282
  },
63283
+ "language": {
63284
+ "type": "string",
63285
+ "description": "First language tag of the browser's `Accept-Language` at the last authentication, e.g. `es-ES`."
63286
+ },
62973
63287
  "device_name": {
62974
63288
  "type": "string",
62975
63289
  "description": "The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."