@insureco/bio 0.5.0 → 0.8.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.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  GraphClient
3
- } from "./chunk-PLN6QPED.mjs";
3
+ } from "./chunk-CKHMGUDP.mjs";
4
+ import {
5
+ PassportClient
6
+ } from "./chunk-UBURAGWI.mjs";
4
7
  import {
5
8
  BioError,
6
9
  parseJsonResponse,
@@ -100,6 +103,23 @@ var BioAuth = class _BioAuth {
100
103
  codeChallenge
101
104
  };
102
105
  }
106
+ /**
107
+ * Build an authorization URL with prompt=none for silent authentication.
108
+ *
109
+ * Useful for checking if the user has an existing session without showing
110
+ * a login screen. If the user is not authenticated, Bio-ID redirects back
111
+ * with an `error=login_required` query parameter instead of showing UI.
112
+ */
113
+ silentAuth(opts) {
114
+ const result = this.getAuthorizationUrl({
115
+ redirectUri: opts.redirectUri,
116
+ scopes: opts.scopes,
117
+ state: opts.state
118
+ });
119
+ const url = new URL(result.url);
120
+ url.searchParams.set("prompt", "none");
121
+ return { ...result, url: url.toString() };
122
+ }
103
123
  /**
104
124
  * Exchange an authorization code for tokens.
105
125
  *
@@ -515,6 +535,257 @@ var BioAdmin = class _BioAdmin {
515
535
  }
516
536
  };
517
537
 
538
+ // src/embed.ts
539
+ var DEFAULT_BIO_URL = "https://bio.tawa.pro";
540
+ var DEFAULT_TIMEOUT_MS3 = 1e4;
541
+ var EmbedClient = class _EmbedClient {
542
+ bioIdUrl;
543
+ clientId;
544
+ clientSecret;
545
+ retries;
546
+ timeoutMs;
547
+ constructor(config) {
548
+ if (!config.clientId) {
549
+ throw new BioError("clientId is required", "config_error");
550
+ }
551
+ if (!config.clientSecret) {
552
+ throw new BioError("clientSecret is required", "config_error");
553
+ }
554
+ this.clientId = config.clientId;
555
+ this.clientSecret = config.clientSecret;
556
+ this.bioIdUrl = (config.bioIdUrl ?? DEFAULT_BIO_URL).replace(/\/$/, "");
557
+ this.retries = config.retries ?? 2;
558
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
559
+ }
560
+ /**
561
+ * Create an EmbedClient from environment variables.
562
+ *
563
+ * Reads: BIO_CLIENT_ID, BIO_CLIENT_SECRET, BIO_ID_URL
564
+ */
565
+ static fromEnv(overrides) {
566
+ const clientId = overrides?.clientId ?? process.env.BIO_CLIENT_ID;
567
+ const clientSecret = overrides?.clientSecret ?? process.env.BIO_CLIENT_SECRET;
568
+ if (!clientId) {
569
+ throw new BioError(
570
+ "BIO_CLIENT_ID environment variable is required",
571
+ "config_error"
572
+ );
573
+ }
574
+ if (!clientSecret) {
575
+ throw new BioError(
576
+ "BIO_CLIENT_SECRET environment variable is required",
577
+ "config_error"
578
+ );
579
+ }
580
+ return new _EmbedClient({
581
+ clientId,
582
+ clientSecret,
583
+ bioIdUrl: overrides?.bioIdUrl ?? process.env.BIO_ID_URL,
584
+ retries: overrides?.retries,
585
+ timeoutMs: overrides?.timeoutMs
586
+ });
587
+ }
588
+ /**
589
+ * Authenticate a user with email and password.
590
+ *
591
+ * @param params - Email and password
592
+ * @returns Access token, refresh token, user profile, and optional branding
593
+ */
594
+ async login(params) {
595
+ if (!params.email) throw new BioError("email is required", "validation_error");
596
+ if (!params.password) throw new BioError("password is required", "validation_error");
597
+ return this.embedRequest("/api/embed/login", {
598
+ email: params.email,
599
+ password: params.password
600
+ });
601
+ }
602
+ /**
603
+ * Create a new user account.
604
+ *
605
+ * @param params - Email, password, name, and optional invite token
606
+ * @returns Access token, refresh token, user profile, and optional branding
607
+ */
608
+ async signup(params) {
609
+ if (!params.email) throw new BioError("email is required", "validation_error");
610
+ if (!params.password) throw new BioError("password is required", "validation_error");
611
+ if (!params.name) throw new BioError("name is required", "validation_error");
612
+ const body = {
613
+ email: params.email,
614
+ password: params.password,
615
+ name: params.name
616
+ };
617
+ if (params.inviteToken) {
618
+ body.inviteToken = params.inviteToken;
619
+ }
620
+ return this.embedRequest("/api/embed/signup", body);
621
+ }
622
+ /**
623
+ * Send a magic link email to the user.
624
+ *
625
+ * The user clicks the link to authenticate without a password.
626
+ * After sending, use `verify()` with the token from the link.
627
+ *
628
+ * @param params - Email address to send the magic link to
629
+ */
630
+ async sendMagicLink(params) {
631
+ if (!params.email) throw new BioError("email is required", "validation_error");
632
+ const response = await this.fetchWithRetry(
633
+ "POST",
634
+ `${this.bioIdUrl}/api/embed/magic-link`,
635
+ JSON.stringify({ email: params.email })
636
+ );
637
+ const json = await parseJsonResponse(response);
638
+ if (!response.ok) {
639
+ throw new BioError(
640
+ extractErrorMessage(json, response.status),
641
+ extractErrorCode(json),
642
+ response.status,
643
+ json
644
+ );
645
+ }
646
+ }
647
+ /**
648
+ * Verify a magic link token and exchange it for auth tokens.
649
+ *
650
+ * @param params - The token from the magic link
651
+ * @returns Access token, refresh token, user profile, and optional branding
652
+ */
653
+ async verify(params) {
654
+ if (!params.token) throw new BioError("token is required", "validation_error");
655
+ return this.embedRequest("/api/embed/verify", {
656
+ token: params.token
657
+ });
658
+ }
659
+ /**
660
+ * Refresh an expired access token using a refresh token.
661
+ *
662
+ * @param params - The refresh token to exchange
663
+ * @returns New access token, rotated refresh token, user profile, and optional branding
664
+ */
665
+ async refresh(params) {
666
+ if (!params.refreshToken) throw new BioError("refreshToken is required", "validation_error");
667
+ return this.embedRequest("/api/embed/refresh", {
668
+ refreshToken: params.refreshToken
669
+ });
670
+ }
671
+ /**
672
+ * Revoke a refresh token (logout).
673
+ *
674
+ * @param params - The refresh token to revoke
675
+ */
676
+ async logout(params) {
677
+ if (!params.refreshToken) throw new BioError("refreshToken is required", "validation_error");
678
+ const response = await this.fetchWithRetry(
679
+ "POST",
680
+ `${this.bioIdUrl}/api/embed/logout`,
681
+ JSON.stringify({ refreshToken: params.refreshToken })
682
+ );
683
+ const json = await parseJsonResponse(response);
684
+ if (!response.ok) {
685
+ throw new BioError(
686
+ extractErrorMessage(json, response.status),
687
+ extractErrorCode(json),
688
+ response.status,
689
+ json
690
+ );
691
+ }
692
+ }
693
+ // ── Private helpers ──────────────────────────────────────────────────────
694
+ async embedRequest(path, body) {
695
+ const response = await this.fetchWithRetry(
696
+ "POST",
697
+ `${this.bioIdUrl}${path}`,
698
+ JSON.stringify(body)
699
+ );
700
+ const json = await parseJsonResponse(response);
701
+ if (!response.ok) {
702
+ throw new BioError(
703
+ extractErrorMessage(json, response.status),
704
+ extractErrorCode(json),
705
+ response.status,
706
+ json
707
+ );
708
+ }
709
+ return mapEmbedResponse(json);
710
+ }
711
+ async fetchWithRetry(method, url, body, attempt = 0) {
712
+ try {
713
+ const response = await fetch(url, {
714
+ method,
715
+ headers: {
716
+ "Content-Type": "application/json",
717
+ "X-Client-Id": this.clientId,
718
+ "X-Client-Secret": this.clientSecret
719
+ },
720
+ body,
721
+ signal: AbortSignal.timeout(this.timeoutMs)
722
+ });
723
+ if (response.status >= 500 && attempt < this.retries) {
724
+ await sleep(retryDelay(attempt));
725
+ return this.fetchWithRetry(method, url, body, attempt + 1);
726
+ }
727
+ return response;
728
+ } catch (err) {
729
+ if (attempt < this.retries) {
730
+ await sleep(retryDelay(attempt));
731
+ return this.fetchWithRetry(method, url, body, attempt + 1);
732
+ }
733
+ const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
734
+ throw new BioError(
735
+ isTimeout ? `Request timed out after ${this.timeoutMs}ms` : err instanceof Error ? err.message : "Network error",
736
+ isTimeout ? "timeout" : "network_error"
737
+ );
738
+ }
739
+ }
740
+ };
741
+ function mapEmbedResponse(raw) {
742
+ const data = raw.data ?? raw;
743
+ const rawUser = data.user ?? {};
744
+ const rawBranding = data.branding;
745
+ const user = {
746
+ bioId: rawUser.bioId,
747
+ email: rawUser.email,
748
+ name: rawUser.name,
749
+ orgSlug: rawUser.orgSlug
750
+ };
751
+ const result = {
752
+ accessToken: data.access_token,
753
+ refreshToken: data.refresh_token,
754
+ tokenType: data.token_type ?? "Bearer",
755
+ expiresIn: data.expires_in,
756
+ user
757
+ };
758
+ if (rawBranding) {
759
+ result.branding = {
760
+ displayName: rawBranding.displayName,
761
+ logoUrl: rawBranding.logoUrl,
762
+ logoMarkUrl: rawBranding.logoMarkUrl,
763
+ primaryColor: rawBranding.primaryColor,
764
+ secondaryColor: rawBranding.secondaryColor,
765
+ verified: rawBranding.verified,
766
+ whiteLabelApproved: rawBranding.whiteLabelApproved
767
+ };
768
+ }
769
+ return result;
770
+ }
771
+ function extractErrorMessage(json, status) {
772
+ const error = json.error;
773
+ if (typeof error === "object" && error !== null) {
774
+ return error.message ?? `Embed API returned ${status}`;
775
+ }
776
+ if (typeof error === "string") {
777
+ return error;
778
+ }
779
+ return `Embed API returned ${status}`;
780
+ }
781
+ function extractErrorCode(json) {
782
+ const error = json.error;
783
+ if (typeof error === "object" && error !== null) {
784
+ return error.code ?? "embed_error";
785
+ }
786
+ return "embed_error";
787
+ }
788
+
518
789
  // src/jwt.ts
519
790
  import crypto3 from "crypto";
520
791
  var DEFAULT_ISSUERS = [
@@ -671,7 +942,9 @@ export {
671
942
  BioAdmin,
672
943
  BioAuth,
673
944
  BioError,
945
+ EmbedClient,
674
946
  GraphClient,
947
+ PassportClient,
675
948
  decodeToken,
676
949
  generatePKCE,
677
950
  isTokenExpired,
@@ -0,0 +1,27 @@
1
+ import { P as Passport, a as PassportStatus } from './passport-types-bPgjNxv-.mjs';
2
+
3
+ interface UsePassportOptions {
4
+ /** Bio-ID base URL */
5
+ bioIdUrl: string;
6
+ /** Access token for authentication */
7
+ accessToken: string | null;
8
+ /** Service name for tracking */
9
+ service?: string;
10
+ /** Auto-reconnect (default: true) */
11
+ autoReconnect?: boolean;
12
+ }
13
+ interface UsePassportResult {
14
+ /** Current passport object (null until identity received) */
15
+ passport: Passport | null;
16
+ /** Connection status */
17
+ status: PassportStatus;
18
+ /** Last error (null if none) */
19
+ error: Error | null;
20
+ /** Manually disconnect */
21
+ disconnect: () => void;
22
+ /** Manually reconnect */
23
+ reconnect: () => void;
24
+ }
25
+ declare function usePassport(options: UsePassportOptions): UsePassportResult;
26
+
27
+ export { type UsePassportOptions, type UsePassportResult, usePassport };
@@ -0,0 +1,27 @@
1
+ import { P as Passport, a as PassportStatus } from './passport-types-bPgjNxv-.js';
2
+
3
+ interface UsePassportOptions {
4
+ /** Bio-ID base URL */
5
+ bioIdUrl: string;
6
+ /** Access token for authentication */
7
+ accessToken: string | null;
8
+ /** Service name for tracking */
9
+ service?: string;
10
+ /** Auto-reconnect (default: true) */
11
+ autoReconnect?: boolean;
12
+ }
13
+ interface UsePassportResult {
14
+ /** Current passport object (null until identity received) */
15
+ passport: Passport | null;
16
+ /** Connection status */
17
+ status: PassportStatus;
18
+ /** Last error (null if none) */
19
+ error: Error | null;
20
+ /** Manually disconnect */
21
+ disconnect: () => void;
22
+ /** Manually reconnect */
23
+ reconnect: () => void;
24
+ }
25
+ declare function usePassport(options: UsePassportOptions): UsePassportResult;
26
+
27
+ export { type UsePassportOptions, type UsePassportResult, usePassport };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/passport-react.ts
21
+ var passport_react_exports = {};
22
+ __export(passport_react_exports, {
23
+ usePassport: () => usePassport
24
+ });
25
+ module.exports = __toCommonJS(passport_react_exports);
26
+ var import_react = require("react");
27
+ function usePassport(options) {
28
+ const { bioIdUrl, accessToken, service, autoReconnect = true } = options;
29
+ const [passport, setPassport] = (0, import_react.useState)(null);
30
+ const [status, setStatus] = (0, import_react.useState)("disconnected");
31
+ const [error, setError] = (0, import_react.useState)(null);
32
+ const wsRef = (0, import_react.useRef)(null);
33
+ const reconnectTimerRef = (0, import_react.useRef)(null);
34
+ const reconnectAttemptRef = (0, import_react.useRef)(0);
35
+ const closedRef = (0, import_react.useRef)(false);
36
+ const cleanup = (0, import_react.useCallback)(() => {
37
+ if (reconnectTimerRef.current) {
38
+ clearTimeout(reconnectTimerRef.current);
39
+ reconnectTimerRef.current = null;
40
+ }
41
+ if (wsRef.current) {
42
+ wsRef.current.onclose = null;
43
+ wsRef.current.onerror = null;
44
+ wsRef.current.onmessage = null;
45
+ wsRef.current.close();
46
+ wsRef.current = null;
47
+ }
48
+ }, []);
49
+ const connect = (0, import_react.useCallback)(() => {
50
+ if (!accessToken || !bioIdUrl || closedRef.current) return;
51
+ cleanup();
52
+ setStatus("connecting");
53
+ setError(null);
54
+ const url = new URL("/passport", bioIdUrl.replace(/^http/, "ws"));
55
+ url.searchParams.set("token", accessToken);
56
+ url.searchParams.set("version", "1");
57
+ if (service) url.searchParams.set("service", service);
58
+ const ws = new WebSocket(url.toString());
59
+ wsRef.current = ws;
60
+ ws.onopen = () => {
61
+ reconnectAttemptRef.current = 0;
62
+ setStatus("connected");
63
+ };
64
+ ws.onmessage = (event) => {
65
+ try {
66
+ const data = JSON.parse(event.data);
67
+ if (data.type === "identity" || data.type === "passport_updated") {
68
+ setPassport(data.passport ?? null);
69
+ } else if (data.type === "revoked") {
70
+ setPassport(null);
71
+ }
72
+ } catch {
73
+ }
74
+ };
75
+ ws.onclose = () => {
76
+ setStatus("disconnected");
77
+ if (!closedRef.current && autoReconnect) {
78
+ const delay = Math.min(1e3 * Math.pow(2, reconnectAttemptRef.current), 3e4);
79
+ reconnectAttemptRef.current++;
80
+ reconnectTimerRef.current = setTimeout(() => {
81
+ reconnectTimerRef.current = null;
82
+ connect();
83
+ }, delay);
84
+ }
85
+ };
86
+ ws.onerror = () => {
87
+ setError(new Error("Passport WebSocket error"));
88
+ setStatus("error");
89
+ };
90
+ }, [bioIdUrl, accessToken, service, autoReconnect, cleanup]);
91
+ (0, import_react.useEffect)(() => {
92
+ closedRef.current = false;
93
+ connect();
94
+ return () => {
95
+ closedRef.current = true;
96
+ cleanup();
97
+ setStatus("disconnected");
98
+ };
99
+ }, [connect, cleanup]);
100
+ const disconnect = (0, import_react.useCallback)(() => {
101
+ closedRef.current = true;
102
+ cleanup();
103
+ setStatus("disconnected");
104
+ }, [cleanup]);
105
+ const reconnect = (0, import_react.useCallback)(() => {
106
+ closedRef.current = false;
107
+ reconnectAttemptRef.current = 0;
108
+ connect();
109
+ }, [connect]);
110
+ return { passport, status, error, disconnect, reconnect };
111
+ }
112
+ // Annotate the CommonJS export names for ESM import in node:
113
+ 0 && (module.exports = {
114
+ usePassport
115
+ });
@@ -0,0 +1,90 @@
1
+ // src/passport-react.ts
2
+ import { useState, useEffect, useRef, useCallback } from "react";
3
+ function usePassport(options) {
4
+ const { bioIdUrl, accessToken, service, autoReconnect = true } = options;
5
+ const [passport, setPassport] = useState(null);
6
+ const [status, setStatus] = useState("disconnected");
7
+ const [error, setError] = useState(null);
8
+ const wsRef = useRef(null);
9
+ const reconnectTimerRef = useRef(null);
10
+ const reconnectAttemptRef = useRef(0);
11
+ const closedRef = useRef(false);
12
+ const cleanup = useCallback(() => {
13
+ if (reconnectTimerRef.current) {
14
+ clearTimeout(reconnectTimerRef.current);
15
+ reconnectTimerRef.current = null;
16
+ }
17
+ if (wsRef.current) {
18
+ wsRef.current.onclose = null;
19
+ wsRef.current.onerror = null;
20
+ wsRef.current.onmessage = null;
21
+ wsRef.current.close();
22
+ wsRef.current = null;
23
+ }
24
+ }, []);
25
+ const connect = useCallback(() => {
26
+ if (!accessToken || !bioIdUrl || closedRef.current) return;
27
+ cleanup();
28
+ setStatus("connecting");
29
+ setError(null);
30
+ const url = new URL("/passport", bioIdUrl.replace(/^http/, "ws"));
31
+ url.searchParams.set("token", accessToken);
32
+ url.searchParams.set("version", "1");
33
+ if (service) url.searchParams.set("service", service);
34
+ const ws = new WebSocket(url.toString());
35
+ wsRef.current = ws;
36
+ ws.onopen = () => {
37
+ reconnectAttemptRef.current = 0;
38
+ setStatus("connected");
39
+ };
40
+ ws.onmessage = (event) => {
41
+ try {
42
+ const data = JSON.parse(event.data);
43
+ if (data.type === "identity" || data.type === "passport_updated") {
44
+ setPassport(data.passport ?? null);
45
+ } else if (data.type === "revoked") {
46
+ setPassport(null);
47
+ }
48
+ } catch {
49
+ }
50
+ };
51
+ ws.onclose = () => {
52
+ setStatus("disconnected");
53
+ if (!closedRef.current && autoReconnect) {
54
+ const delay = Math.min(1e3 * Math.pow(2, reconnectAttemptRef.current), 3e4);
55
+ reconnectAttemptRef.current++;
56
+ reconnectTimerRef.current = setTimeout(() => {
57
+ reconnectTimerRef.current = null;
58
+ connect();
59
+ }, delay);
60
+ }
61
+ };
62
+ ws.onerror = () => {
63
+ setError(new Error("Passport WebSocket error"));
64
+ setStatus("error");
65
+ };
66
+ }, [bioIdUrl, accessToken, service, autoReconnect, cleanup]);
67
+ useEffect(() => {
68
+ closedRef.current = false;
69
+ connect();
70
+ return () => {
71
+ closedRef.current = true;
72
+ cleanup();
73
+ setStatus("disconnected");
74
+ };
75
+ }, [connect, cleanup]);
76
+ const disconnect = useCallback(() => {
77
+ closedRef.current = true;
78
+ cleanup();
79
+ setStatus("disconnected");
80
+ }, [cleanup]);
81
+ const reconnect = useCallback(() => {
82
+ closedRef.current = false;
83
+ reconnectAttemptRef.current = 0;
84
+ connect();
85
+ }, [connect]);
86
+ return { passport, status, error, disconnect, reconnect };
87
+ }
88
+ export {
89
+ usePassport
90
+ };
@@ -0,0 +1,79 @@
1
+ /** The full Passport identity object pushed via WebSocket */
2
+ interface Passport {
3
+ bioId: string;
4
+ email: string;
5
+ firstName: string;
6
+ lastName: string;
7
+ orgSlug: string;
8
+ orgId: string;
9
+ roles: string[];
10
+ villages: PassportVillage[];
11
+ serviceGrants: PassportServiceGrant[];
12
+ crossOrgPermissions: PassportCrossOrgPermission[];
13
+ session: PassportSession;
14
+ }
15
+ interface PassportVillage {
16
+ slug: string;
17
+ name: string;
18
+ role: 'member' | 'admin' | 'owner';
19
+ }
20
+ interface PassportServiceGrant {
21
+ service: string;
22
+ scopes: string[];
23
+ grantedAt: string;
24
+ }
25
+ interface PassportCrossOrgPermission {
26
+ targetOrgSlug: string;
27
+ modules: string[];
28
+ }
29
+ interface PassportSession {
30
+ issuedAt: string;
31
+ lastSeen: string;
32
+ connectedServices: string[];
33
+ }
34
+ /** Interface for token refresh — accepts any object with a refreshToken method */
35
+ interface PassportTokenRefresher {
36
+ refreshToken: (refreshToken: string) => Promise<{
37
+ access_token: string;
38
+ refresh_token?: string;
39
+ }>;
40
+ }
41
+ /** Configuration for PassportClient */
42
+ interface PassportClientConfig {
43
+ /** Bio-ID base URL (e.g. https://bio.insureco.io) */
44
+ bioIdUrl: string;
45
+ /** Access token for authentication */
46
+ accessToken: string;
47
+ /** Service name sent as ?service= query param */
48
+ service?: string;
49
+ /** Auto-reconnect on disconnect (default: true) */
50
+ autoReconnect?: boolean;
51
+ /** Max reconnect delay in ms (default: 30000) */
52
+ maxReconnectDelay?: number;
53
+ /** Optional refresh token for auto-refresh on 4003 close */
54
+ refreshToken?: string;
55
+ /** Callback when tokens are refreshed (so app can persist new tokens) */
56
+ onTokenRefresh?: (tokens: {
57
+ access_token: string;
58
+ refresh_token?: string;
59
+ }) => void;
60
+ /** Token refresher (e.g. BioAuth instance) — required if refreshToken is set */
61
+ bioAuth?: PassportTokenRefresher;
62
+ }
63
+ /** Events emitted by the passport socket */
64
+ type PassportEventType = 'identity' | 'passport_updated' | 'revoked';
65
+ /** Message received from the passport WebSocket */
66
+ interface PassportMessage {
67
+ type: PassportEventType;
68
+ passport?: Passport;
69
+ }
70
+ /** Status of the passport connection */
71
+ type PassportStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
72
+ /** Branding config from passport (optional) */
73
+ interface PassportBranding {
74
+ logoUrl?: string;
75
+ primaryColor?: string;
76
+ appName?: string;
77
+ }
78
+
79
+ export type { Passport as P, PassportStatus as a, PassportBranding as b, PassportClientConfig as c, PassportCrossOrgPermission as d, PassportEventType as e, PassportMessage as f, PassportServiceGrant as g, PassportSession as h, PassportTokenRefresher as i, PassportVillage as j };