@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/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/appearance.d.ts +85 -0
- package/dist/appearance.js +98 -0
- package/dist/appearance.js.map +1 -0
- package/dist/components.d.ts +114 -0
- package/dist/components.js +327 -0
- package/dist/components.js.map +1 -0
- package/dist/composables.d.ts +49 -0
- package/dist/composables.js +65 -0
- package/dist/composables.js.map +1 -0
- package/dist/context.d.ts +20 -0
- package/dist/context.js +30 -0
- package/dist/context.js.map +1 -0
- package/dist/fapi.d.ts +11 -0
- package/dist/fapi.js +24 -0
- package/dist/fapi.js.map +1 -0
- package/dist/flow.d.ts +22 -0
- package/dist/flow.js +79 -0
- package/dist/flow.js.map +1 -0
- package/dist/i18n.d.ts +23 -0
- package/dist/i18n.js +83 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +72 -0
- package/dist/plugin.js +251 -0
- package/dist/plugin.js.map +1 -0
- package/dist/protect.d.ts +18 -0
- package/dist/protect.js +24 -0
- package/dist/protect.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
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
|
package/dist/flow.js.map
ADDED
|
@@ -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
|
package/dist/i18n.js.map
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -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';
|
package/dist/protect.js
ADDED
|
@@ -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"}
|