@atlasauth/vue 0.1.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/flow.js ADDED
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFlow = useFlow;
4
+ exports.useSignIn = useSignIn;
5
+ exports.useSignUp = useSignUp;
6
+ const vue_1 = require("vue");
7
+ const fapi_1 = require("./fapi");
8
+ const context_1 = require("./context");
9
+ function useFlow() {
10
+ const atlas = (0, context_1.useAtlas)();
11
+ // Seeded from a redirect that came back mid-flow (e.g. OAuth needing a second
12
+ // factor), so the user resumes at the demanded step instead of starting over.
13
+ const state = (0, vue_1.shallowRef)((0, fapi_1.flowFromPending)(atlas.pendingAttempt));
14
+ const client = new fapi_1.FapiClient({
15
+ publishableKey: atlas.publishableKey,
16
+ baseUrl: atlas.frontendApi,
17
+ });
18
+ const submit = (values) => {
19
+ void (async () => {
20
+ state.value = { ...state.value, busy: true, errors: [] };
21
+ const next = await (0, fapi_1.advance)(client, state.value, values);
22
+ state.value = next;
23
+ // On completion the response carries a one-time ticket (§7.1). Exchange it
24
+ // for cookies over a same-origin request — FAPI is cross-origin to the app
25
+ // and cannot set the cookie on the completion response itself — THEN
26
+ // re-boot so the rest of the app sees a signed-in user.
27
+ if (next.attempt?.status === 'complete') {
28
+ if (next.ticket) {
29
+ await client.post('/v1/client/tickets/exchange', {
30
+ attempt_id: next.attempt.id,
31
+ ticket: next.ticket,
32
+ });
33
+ }
34
+ await atlas.reload();
35
+ }
36
+ })();
37
+ };
38
+ // §5.3: collect a sign-in completed by a link opened on another device. The
39
+ // poll restarts whenever the flow state changes and only runs while the state
40
+ // says it should — the Vue peer of React's polling effect.
41
+ let poller = null;
42
+ const stopPolling = () => {
43
+ if (poller) {
44
+ clearInterval(poller);
45
+ poller = null;
46
+ }
47
+ };
48
+ (0, vue_1.watch)(state, (current) => {
49
+ stopPolling();
50
+ if (!(0, fapi_1.shouldKeepPolling)(current))
51
+ return;
52
+ poller = setInterval(() => {
53
+ void (async () => {
54
+ const { state: next, ticket } = await (0, fapi_1.pollAttempt)(client, state.value);
55
+ state.value = next;
56
+ if (!ticket || !next.attempt)
57
+ return;
58
+ // The ticket is exchanged by a normal request, so no session token has
59
+ // ever appeared in a URL.
60
+ await client.post('/v1/client/tickets/exchange', {
61
+ attempt_id: next.attempt.id,
62
+ ticket,
63
+ });
64
+ await atlas.reload();
65
+ })();
66
+ }, 2_000);
67
+ }, { immediate: true });
68
+ (0, vue_1.onScopeDispose)(stopPolling);
69
+ return { state, submit };
70
+ }
71
+ /** §10.2 `useSignIn()` — drive a sign-in attempt. */
72
+ function useSignIn() {
73
+ return useFlow();
74
+ }
75
+ /** §10.2 `useSignUp()` — drive a sign-up attempt. */
76
+ function useSignUp() {
77
+ return useFlow();
78
+ }
79
+ //# sourceMappingURL=flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../src/flow.ts"],"names":[],"mappings":"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"}
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * §10.3: "Every string passes through an i18n catalog; en-US ships, catalog
3
+ * format documented for community locales."
4
+ *
5
+ * A flat key-to-string map rather than nested objects, because nesting looks
6
+ * tidier and makes a missing key a runtime crash on `undefined.of.a.chain`
7
+ * rather than a visible fallback.
8
+ *
9
+ * A missing key renders the KEY, not an empty string. A blank button is a bug
10
+ * nobody can report; a button reading `signIn.submit` is one anyone can.
11
+ */
12
+ export type Catalog = Record<string, string>;
13
+ export declare const EN_US: Catalog;
14
+ /**
15
+ * Look up a key and interpolate `{placeholders}`.
16
+ *
17
+ * An unknown placeholder is left visible for the same reason a missing key is:
18
+ * `Continue with {provider}` in the UI is a bug report waiting to be filed,
19
+ * whereas `Continue with ` is one nobody notices until a customer does.
20
+ */
21
+ export declare function translate(catalog: Catalog, key: string, variables?: Record<string, string>): string;
22
+ /** Merge a community locale over en-US so a partial translation still renders. */
23
+ export declare function withFallback(locale: Catalog): Catalog;
package/dist/i18n.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * §10.3: "Every string passes through an i18n catalog; en-US ships, catalog
4
+ * format documented for community locales."
5
+ *
6
+ * A flat key-to-string map rather than nested objects, because nesting looks
7
+ * tidier and makes a missing key a runtime crash on `undefined.of.a.chain`
8
+ * rather than a visible fallback.
9
+ *
10
+ * A missing key renders the KEY, not an empty string. A blank button is a bug
11
+ * nobody can report; a button reading `signIn.submit` is one anyone can.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.EN_US = void 0;
15
+ exports.translate = translate;
16
+ exports.withFallback = withFallback;
17
+ exports.EN_US = {
18
+ 'signIn.title': 'Sign in',
19
+ 'signIn.subtitle': 'to continue to {application}',
20
+ 'signIn.identifierLabel': 'Email address',
21
+ 'signIn.passwordLabel': 'Password',
22
+ 'signIn.submit': 'Continue',
23
+ 'signIn.forgotPassword': 'Forgot password?',
24
+ 'signIn.noAccount': 'No account?',
25
+ 'signIn.signUpLink': 'Sign up',
26
+ 'signIn.orDivider': 'or',
27
+ 'signIn.socialButton': 'Continue with {provider}',
28
+ 'signIn.emailCodeLabel': 'Verification code',
29
+ 'signIn.magicLinkSent': 'Check your email for a sign-in link.',
30
+ 'signIn.checkOtherTab': 'You can close this tab — you are signed in where you started.',
31
+ 'signUp.title': 'Create your account',
32
+ 'signUp.submit': 'Continue',
33
+ 'signUp.haveAccount': 'Already have an account?',
34
+ 'signUp.signInLink': 'Sign in',
35
+ 'mfa.title': 'Two-step verification',
36
+ 'mfa.subtitle': 'Enter the code from your authenticator app.',
37
+ 'mfa.codeLabel': 'Code',
38
+ 'mfa.enrollFirstCodeLabel': 'Code from your authenticator app',
39
+ 'mfa.enrollSecondCodeLabel': 'The next code it shows',
40
+ 'mfa.usePasskey': 'Use a passkey instead',
41
+ 'mfa.useBackupCode': 'Use a recovery code',
42
+ 'mfa.submit': 'Verify',
43
+ 'reset.title': 'Reset your password',
44
+ 'reset.submit': 'Send reset code',
45
+ 'reset.newPasswordLabel': 'New password',
46
+ 'userButton.manageAccount': 'Manage account',
47
+ 'userButton.signOut': 'Sign out',
48
+ 'userButton.signOutAll': 'Sign out of all devices',
49
+ 'userProfile.title': 'Account',
50
+ 'userProfile.profileSection': 'Profile',
51
+ 'userProfile.emailsSection': 'Email addresses',
52
+ 'userProfile.securitySection': 'Security',
53
+ 'userProfile.devicesSection': 'Active devices',
54
+ 'userProfile.dangerSection': 'Danger zone',
55
+ 'userProfile.addEmail': 'Add an email address',
56
+ 'userProfile.makePrimary': 'Make primary',
57
+ 'userProfile.unverified': 'Unverified',
58
+ 'userProfile.revokeDevice': 'Sign out',
59
+ 'userProfile.deleteAccount': 'Delete account',
60
+ 'organization.switcher': 'Switch organization',
61
+ 'organization.personal': 'Personal account',
62
+ 'organization.members': 'Members',
63
+ 'organization.invite': 'Invite member',
64
+ 'organization.roleLabel': 'Role',
65
+ 'error.generic': 'Something went wrong. Please try again.',
66
+ 'error.network': 'We could not reach the server. Check your connection.',
67
+ };
68
+ /**
69
+ * Look up a key and interpolate `{placeholders}`.
70
+ *
71
+ * An unknown placeholder is left visible for the same reason a missing key is:
72
+ * `Continue with {provider}` in the UI is a bug report waiting to be filed,
73
+ * whereas `Continue with ` is one nobody notices until a customer does.
74
+ */
75
+ function translate(catalog, key, variables = {}) {
76
+ const template = catalog[key] ?? exports.EN_US[key] ?? key;
77
+ return template.replace(/\{(\w+)\}/g, (match, name) => variables[name] ?? match);
78
+ }
79
+ /** Merge a community locale over en-US so a partial translation still renders. */
80
+ function withFallback(locale) {
81
+ return { ...exports.EN_US, ...locale };
82
+ }
83
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAsEH,8BAOC;AAGD,oCAEC;AA9EY,QAAA,KAAK,GAAY;IAC5B,cAAc,EAAE,SAAS;IACzB,iBAAiB,EAAE,8BAA8B;IACjD,wBAAwB,EAAE,eAAe;IACzC,sBAAsB,EAAE,UAAU;IAClC,eAAe,EAAE,UAAU;IAC3B,uBAAuB,EAAE,kBAAkB;IAC3C,kBAAkB,EAAE,aAAa;IACjC,mBAAmB,EAAE,SAAS;IAC9B,kBAAkB,EAAE,IAAI;IACxB,qBAAqB,EAAE,0BAA0B;IACjD,uBAAuB,EAAE,mBAAmB;IAC5C,sBAAsB,EAAE,sCAAsC;IAC9D,sBAAsB,EAAE,+DAA+D;IAEvF,cAAc,EAAE,qBAAqB;IACrC,eAAe,EAAE,UAAU;IAC3B,oBAAoB,EAAE,0BAA0B;IAChD,mBAAmB,EAAE,SAAS;IAE9B,WAAW,EAAE,uBAAuB;IACpC,cAAc,EAAE,6CAA6C;IAC7D,eAAe,EAAE,MAAM;IACvB,0BAA0B,EAAE,kCAAkC;IAC9D,2BAA2B,EAAE,wBAAwB;IACrD,gBAAgB,EAAE,uBAAuB;IACzC,mBAAmB,EAAE,qBAAqB;IAC1C,YAAY,EAAE,QAAQ;IAEtB,aAAa,EAAE,qBAAqB;IACpC,cAAc,EAAE,iBAAiB;IACjC,wBAAwB,EAAE,cAAc;IAExC,0BAA0B,EAAE,gBAAgB;IAC5C,oBAAoB,EAAE,UAAU;IAChC,uBAAuB,EAAE,yBAAyB;IAElD,mBAAmB,EAAE,SAAS;IAC9B,4BAA4B,EAAE,SAAS;IACvC,2BAA2B,EAAE,iBAAiB;IAC9C,6BAA6B,EAAE,UAAU;IACzC,4BAA4B,EAAE,gBAAgB;IAC9C,2BAA2B,EAAE,aAAa;IAC1C,sBAAsB,EAAE,sBAAsB;IAC9C,yBAAyB,EAAE,cAAc;IACzC,wBAAwB,EAAE,YAAY;IACtC,0BAA0B,EAAE,UAAU;IACtC,2BAA2B,EAAE,gBAAgB;IAE7C,uBAAuB,EAAE,qBAAqB;IAC9C,uBAAuB,EAAE,kBAAkB;IAC3C,sBAAsB,EAAE,SAAS;IACjC,qBAAqB,EAAE,eAAe;IACtC,wBAAwB,EAAE,MAAM;IAEhC,eAAe,EAAE,yCAAyC;IAC1D,eAAe,EAAE,uDAAuD;CACzE,CAAC;AAEF;;;;;;GAMG;AACH,SAAgB,SAAS,CACvB,OAAgB,EAChB,GAAW,EACX,YAAoC,EAAE;IAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,aAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IACnD,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;AAC3F,CAAC;AAED,kFAAkF;AAClF,SAAgB,YAAY,CAAC,MAAe;IAC1C,OAAO,EAAE,GAAG,aAAK,EAAE,GAAG,MAAM,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export * from './appearance';
2
+ export * from './protect';
3
+ export * from './i18n';
4
+ export * from './types';
5
+ export * from './plugin';
6
+ export * from './context';
7
+ export * from './composables';
8
+ export * from './flow';
9
+ export * from './fapi';
10
+ export * from './components';
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./appearance"), exports);
18
+ __exportStar(require("./protect"), exports);
19
+ __exportStar(require("./i18n"), exports);
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./plugin"), exports);
22
+ __exportStar(require("./context"), exports);
23
+ __exportStar(require("./composables"), exports);
24
+ __exportStar(require("./flow"), exports);
25
+ __exportStar(require("./fapi"), exports);
26
+ __exportStar(require("./components"), exports);
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,4CAA0B;AAC1B,yCAAuB;AACvB,0CAAwB;AACxB,2CAAyB;AACzB,4CAA0B;AAC1B,gDAA8B;AAC9B,yCAAuB;AACvB,yCAAuB;AACvB,+CAA6B"}
@@ -0,0 +1,72 @@
1
+ import { type Plugin, type ShallowRef } from 'vue';
2
+ import { type ProviderToken } from '@atlasauth/js';
3
+ import { type Appearance } from './appearance';
4
+ import { type Catalog } from './i18n';
5
+ import type { AtlasOrganizationMembership, AtlasSession, AtlasUser, SessionClaims } from './types';
6
+ /**
7
+ * §10.1 `app.use(createAtlas({ publishableKey }))`.
8
+ *
9
+ * The Vue peer of React's `<AtlasProvider>`. Holds exactly one thing that
10
+ * matters: the token, in memory. Not localStorage, not a cookie this code
11
+ * writes — the refresh token is HttpOnly so browser script never touches it,
12
+ * and the short-lived JWT lives only as long as the tab. A client that
13
+ * persisted either would survive the tab and become the thing an XSS reads
14
+ * first.
15
+ */
16
+ export type AuthStatus = 'loading' | 'signed_in' | 'signed_out';
17
+ /**
18
+ * The reactive auth state + actions provided to the whole app. Every field the
19
+ * React `AtlasContextValue` exposes, but the mutable ones are Vue refs so a
20
+ * composable can hand back a live `computed` rather than a snapshot.
21
+ */
22
+ export interface AtlasClient {
23
+ publishableKey: string;
24
+ /** The instance's FAPI origin. */
25
+ frontendApi: string;
26
+ appearance?: Appearance;
27
+ catalog: Catalog;
28
+ status: ShallowRef<AuthStatus>;
29
+ user: ShallowRef<AtlasUser | null>;
30
+ session: ShallowRef<AtlasSession | null>;
31
+ claims: ShallowRef<SessionClaims | null>;
32
+ memberships: ShallowRef<AtlasOrganizationMembership[]>;
33
+ /**
34
+ * §5.3: a sign-in attempt handed back by an OAuth redirect that still needs a
35
+ * second factor (no session was issued). Null in the ordinary case. The
36
+ * <SignIn> flow seeds itself from this so the user resumes at the 2FA step
37
+ * instead of starting over. Captured synchronously at install time — before
38
+ * any component reads it — because a `needs_second_factor` redirect carries a
39
+ * status but no ticket, and that is what the 2FA resume seeds from.
40
+ */
41
+ pendingAttempt: {
42
+ id: string;
43
+ status: string;
44
+ } | null;
45
+ /** Always fresh: refreshes first if the cached token is close to expiry. */
46
+ getToken(): Promise<string | null>;
47
+ /**
48
+ * §6.6 the signed-in user's OWN live token at a connected provider (Google,
49
+ * GitHub, …), so the app can call that provider's API. Null when there is no
50
+ * usable token — no linked account, or the provider granted / kept none. The
51
+ * server refreshes a stale token on read; the refresh token is never exposed.
52
+ */
53
+ getProviderToken(provider: string): Promise<ProviderToken | null>;
54
+ signOut(): Promise<void>;
55
+ setActiveOrganization(organizationId: string | null): Promise<void>;
56
+ reload(): Promise<void>;
57
+ }
58
+ export interface CreateAtlasOptions {
59
+ publishableKey: string;
60
+ /** The instance's FAPI origin. */
61
+ frontendApi?: string;
62
+ appearance?: Appearance;
63
+ localization?: Catalog;
64
+ fetchImpl?: typeof fetch;
65
+ }
66
+ /**
67
+ * Build the Vue plugin. `app.use(createAtlas({ publishableKey }))` installs the
68
+ * client, provides it for `inject`, paints the appearance tokens onto the host
69
+ * DOM as CSS variables (no iframe — components render in the host DOM so
70
+ * Tailwind/CSS just works), and boots the session.
71
+ */
72
+ export declare function createAtlas(options: CreateAtlasOptions): Plugin;
package/dist/plugin.js ADDED
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAtlas = createAtlas;
4
+ const vue_1 = require("vue");
5
+ const js_1 = require("@atlasauth/js");
6
+ const appearance_1 = require("./appearance");
7
+ const i18n_1 = require("./i18n");
8
+ const context_1 = require("./context");
9
+ /**
10
+ * Build the Vue plugin. `app.use(createAtlas({ publishableKey }))` installs the
11
+ * client, provides it for `inject`, paints the appearance tokens onto the host
12
+ * DOM as CSS variables (no iframe — components render in the host DOM so
13
+ * Tailwind/CSS just works), and boots the session.
14
+ */
15
+ function createAtlas(options) {
16
+ const { client, boot } = createAtlasClient(options);
17
+ return {
18
+ install(app) {
19
+ app.provide(context_1.ATLAS_INJECTION_KEY, client);
20
+ // Tokens land as CSS variables on the host document root — the Vue peer of
21
+ // React's `<div class="atlas-root" style={cssVars}>`. There is no single
22
+ // element wrapping the whole app in a plugin, so the document root inherits
23
+ // them for every component that renders.
24
+ if (typeof document !== 'undefined') {
25
+ const vars = (0, appearance_1.cssVariables)((0, appearance_1.resolveTokens)(options.appearance));
26
+ for (const [name, value] of Object.entries(vars)) {
27
+ document.documentElement.style.setProperty(name, value);
28
+ }
29
+ document.documentElement.classList.add('atlas-root');
30
+ }
31
+ // Boot once per install. Guarded to the browser: on the server there is no
32
+ // redirect to complete and no cookie jar to read, so the app renders
33
+ // `loading` and hydrates into the real status on the client.
34
+ if (typeof window !== 'undefined') {
35
+ void boot();
36
+ }
37
+ },
38
+ };
39
+ }
40
+ /**
41
+ * The reactive client. Separated from the plugin wrapper so a test can build it
42
+ * directly, and so a single `createAtlas(...)` result installs one consistent
43
+ * client no matter how many times the returned plugin is `use`d.
44
+ */
45
+ function createAtlasClient(options) {
46
+ const publishableKey = options.publishableKey;
47
+ const baseUrl = options.frontendApi ?? '';
48
+ const doFetch = options.fetchImpl ?? fetch;
49
+ const status = (0, vue_1.shallowRef)('loading');
50
+ const user = (0, vue_1.shallowRef)(null);
51
+ const session = (0, vue_1.shallowRef)(null);
52
+ const claims = (0, vue_1.shallowRef)(null);
53
+ const memberships = (0, vue_1.shallowRef)([]);
54
+ const cache = new js_1.TokenCache();
55
+ let timer = null;
56
+ const pendingAttempt = (() => {
57
+ if (typeof window === 'undefined')
58
+ return null;
59
+ const result = (0, js_1.readRedirectResult)(window.location.search);
60
+ return result?.status && !result.ticket
61
+ ? { id: result.attemptId, status: result.status }
62
+ : null;
63
+ })();
64
+ const call = (path, init) => doFetch(`${baseUrl}${path}`, {
65
+ ...init,
66
+ // Cookies carry the session; a fetch without this looks identical to
67
+ // being signed out.
68
+ credentials: 'include',
69
+ headers: {
70
+ 'x-publishable-key': publishableKey,
71
+ ...(init?.body ? { 'content-type': 'application/json' } : {}),
72
+ ...init?.headers,
73
+ },
74
+ });
75
+ const applyToken = (jwt, sessionId) => {
76
+ const expiresAt = (0, js_1.readExpiry)(jwt);
77
+ if (!expiresAt)
78
+ return;
79
+ cache.set({ jwt, expiresAt, sessionId });
80
+ claims.value = decodeClaims(jwt);
81
+ };
82
+ const reload = async () => {
83
+ try {
84
+ const response = await call('/v1/client');
85
+ if (!response.ok)
86
+ throw new Error('boot failed');
87
+ const body = (await response.json());
88
+ if (!body.session || !body.user) {
89
+ cache.clear();
90
+ status.value = 'signed_out';
91
+ user.value = null;
92
+ session.value = null;
93
+ claims.value = null;
94
+ memberships.value = [];
95
+ return;
96
+ }
97
+ user.value = body.user;
98
+ session.value = body.session;
99
+ memberships.value = body.organization_memberships ?? [];
100
+ if (body.jwt)
101
+ applyToken(body.jwt, body.session.id);
102
+ status.value = 'signed_in';
103
+ }
104
+ catch {
105
+ // A failed boot is NOT signed-out. Rendering a sign-in form because the
106
+ // network hiccuped throws away a perfectly good session and makes the
107
+ // user re-authenticate for no reason.
108
+ if (status.value === 'loading')
109
+ status.value = 'signed_out';
110
+ }
111
+ };
112
+ const refresh = async () => {
113
+ const current = cache.peek();
114
+ if (!current)
115
+ return;
116
+ try {
117
+ const response = await call(`/v1/client/sessions/${current.sessionId}/tokens`, {
118
+ method: 'POST',
119
+ });
120
+ if (!response.ok)
121
+ throw new Error('refresh failed');
122
+ const body = (await response.json());
123
+ applyToken(body.jwt, body.session_id);
124
+ }
125
+ catch {
126
+ // The session may genuinely be gone. Re-boot rather than guess.
127
+ await reload();
128
+ }
129
+ };
130
+ /** §7.4: proactive, jittered refresh scheduled from the token's own expiry. */
131
+ const scheduleRefresh = () => {
132
+ if (timer) {
133
+ clearTimeout(timer);
134
+ timer = null;
135
+ }
136
+ if (status.value !== 'signed_in')
137
+ return;
138
+ const token = cache.peek();
139
+ if (!token)
140
+ return;
141
+ const delay = (0, js_1.msUntilRefresh)({ token, now: Date.now() });
142
+ timer = setTimeout(() => void refresh(), delay);
143
+ };
144
+ const getToken = async () => {
145
+ // A 5s skew so a token that would die inside the caller's request is
146
+ // refreshed first rather than handed over.
147
+ const usable = cache.get(5_000);
148
+ if (usable)
149
+ return usable.jwt;
150
+ await refresh();
151
+ return cache.get(5_000)?.jwt ?? null;
152
+ };
153
+ // The FAPI client for the token-vault read. Reuses the same publishable key
154
+ // and fetch so the framework-agnostic `@atlasauth/js` helper is the single
155
+ // implementation the Vue SDK surfaces.
156
+ const fapiClient = new js_1.FapiClient({ publishableKey, baseUrl, fetchImpl: doFetch });
157
+ const getProviderToken = (provider) => (0, js_1.getProviderToken)(fapiClient, provider);
158
+ const signOut = async () => {
159
+ const current = cache.peek();
160
+ if (current) {
161
+ await call(`/v1/client/sessions/${current.sessionId}/revoke`, { method: 'POST' }).catch(() => undefined);
162
+ }
163
+ cache.clear();
164
+ status.value = 'signed_out';
165
+ user.value = null;
166
+ session.value = null;
167
+ claims.value = null;
168
+ memberships.value = [];
169
+ };
170
+ const setActiveOrganization = async (organizationId) => {
171
+ const current = cache.peek();
172
+ if (!current)
173
+ return;
174
+ const response = await call(`/v1/client/sessions/${current.sessionId}/touch`, {
175
+ method: 'POST',
176
+ body: JSON.stringify({ active_organization_id: organizationId }),
177
+ });
178
+ if (!response.ok)
179
+ return;
180
+ const body = (await response.json());
181
+ // The new token carries the new org claims; re-boot would work too but
182
+ // costs a round trip for information already in hand.
183
+ applyToken(body.jwt, current.sessionId);
184
+ await reload();
185
+ };
186
+ const catalog = options.localization ? (0, i18n_1.withFallback)(options.localization) : i18n_1.EN_US;
187
+ // §7.4 the refresh timer is driven off the reactive state. Any change to
188
+ // status or claims (a new token has new claims and a new expiry) reschedules
189
+ // the proactive refresh — the Vue peer of React's expiry effect. A detached
190
+ // scope so the watcher lives for the app's lifetime and never attaches to a
191
+ // component that happens to install the plugin.
192
+ const scope = (0, vue_1.effectScope)(true);
193
+ scope.run(() => {
194
+ (0, vue_1.watch)([status, claims], () => scheduleRefresh(), { flush: 'post' });
195
+ });
196
+ /**
197
+ * §6.3 step 3: complete a redirect-based sign-in. An OAuth or hosted-page flow
198
+ * lands back here with a one-time ticket in the URL; exchange it for cookies,
199
+ * scrub the URL so a reload can't replay it, THEN boot. A redirect that only
200
+ * carries a `__atlas_status` (a second factor is still owed) has no ticket, so
201
+ * nothing is exchanged and the app boots signed-out — exactly as intended.
202
+ */
203
+ const boot = async () => {
204
+ const result = typeof window !== 'undefined' ? (0, js_1.readRedirectResult)(window.location.search) : null;
205
+ if (result?.ticket) {
206
+ await call('/v1/client/tickets/exchange', {
207
+ method: 'POST',
208
+ body: JSON.stringify({ attempt_id: result.attemptId, ticket: result.ticket }),
209
+ }).catch(() => undefined);
210
+ }
211
+ if (result && typeof window !== 'undefined') {
212
+ window.history.replaceState({}, document.title, (0, js_1.stripRedirectParams)(window.location.href));
213
+ }
214
+ await reload();
215
+ };
216
+ // `boot` is returned separately, not on the client — it is an install-time
217
+ // concern the plugin drives exactly once, so the public `AtlasClient` surface
218
+ // stays identical to React's context value.
219
+ return {
220
+ client: {
221
+ publishableKey,
222
+ frontendApi: baseUrl,
223
+ appearance: options.appearance,
224
+ catalog,
225
+ status,
226
+ user,
227
+ session,
228
+ claims,
229
+ memberships,
230
+ pendingAttempt,
231
+ getToken,
232
+ getProviderToken,
233
+ signOut,
234
+ setActiveOrganization,
235
+ reload,
236
+ },
237
+ boot,
238
+ };
239
+ }
240
+ function decodeClaims(jwt) {
241
+ const parts = jwt.split('.');
242
+ if (parts.length !== 3)
243
+ return null;
244
+ try {
245
+ return JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
246
+ }
247
+ catch {
248
+ return null;
249
+ }
250
+ }
251
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * §10.2 `<Protect permission="org:sys_memberships:manage">` — conditional
3
+ * rendering.
4
+ *
5
+ * The rule that makes this safe to ship: this is a RENDERING helper, never an
6
+ * authorization boundary. It decides what a user sees, and seeing is not doing.
7
+ * Anyone can open devtools and flip the condition; the only thing that stops
8
+ * them acting is the server checking the same permission on the request.
9
+ *
10
+ * That is worth stating loudly because the component reads like a guard, and a
11
+ * developer who believes it is one will eventually put a delete button behind
12
+ * it and no check behind the endpoint.
13
+ *
14
+ * The evaluation logic lives in `@atlasauth/authz` — the ONE primitive every SDK
15
+ * (backend, Next.js, React, React Native, JS) shares, so `<Protect>` here and
16
+ * `auth().has()` on the server judge a condition identically.
17
+ */
18
+ export { type ProtectCondition, type ProtectOutcome, type ProtectReason, evaluate, hasFromClaims, } from '@atlasauth/authz';
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasFromClaims = exports.evaluate = void 0;
4
+ /**
5
+ * §10.2 `<Protect permission="org:sys_memberships:manage">` — conditional
6
+ * rendering.
7
+ *
8
+ * The rule that makes this safe to ship: this is a RENDERING helper, never an
9
+ * authorization boundary. It decides what a user sees, and seeing is not doing.
10
+ * Anyone can open devtools and flip the condition; the only thing that stops
11
+ * them acting is the server checking the same permission on the request.
12
+ *
13
+ * That is worth stating loudly because the component reads like a guard, and a
14
+ * developer who believes it is one will eventually put a delete button behind
15
+ * it and no check behind the endpoint.
16
+ *
17
+ * The evaluation logic lives in `@atlasauth/authz` — the ONE primitive every SDK
18
+ * (backend, Next.js, React, React Native, JS) shares, so `<Protect>` here and
19
+ * `auth().has()` on the server judge a condition identically.
20
+ */
21
+ var authz_1 = require("@atlasauth/authz");
22
+ Object.defineProperty(exports, "evaluate", { enumerable: true, get: function () { return authz_1.evaluate; } });
23
+ Object.defineProperty(exports, "hasFromClaims", { enumerable: true, get: function () { return authz_1.hasFromClaims; } });
24
+ //# sourceMappingURL=protect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protect.js","sourceRoot":"","sources":["../src/protect.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,0CAM0B;AAFxB,iGAAA,QAAQ,OAAA;AACR,sGAAA,aAAa,OAAA"}