@atlasauth/svelte 0.6.1 → 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/CHANGELOG.md +15 -0
- package/dist/accessors.js +12 -5
- package/dist/components/AttemptFlow.svelte +8 -1
- package/dist/components/AttemptFlow.svelte.d.ts +1 -0
- package/dist/components/SignIn.svelte +28 -0
- package/dist/components/SignIn.svelte.d.ts +5 -0
- package/dist/components/SignUp.svelte +1 -0
- package/dist/context.d.ts +15 -2
- package/dist/context.js +9 -3
- package/dist/i18n.js +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlasauth/svelte changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `@atlasauth/js` 0.15.0: referral capture for any referral program and any consent manager (pass a custom program's reader page-wide with `configureReferralCapture({ referralSource })`).
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Development instances: sign-in from `localhost` keeps working where the browser blocks the instance's cookie as third-party (Chrome, Safari). On a `pk_test_` key the SDK keeps the session's refresh token in `localStorage` and sends it as `x-refresh-token`; production keys never do.
|
|
12
|
+
- After a sign-in completes, the panel says "You're signed in" only once this browser holds the session, and says plainly when the browser didn't keep it, instead of "you are signed in where you started". That message is now shown only for a sign-in completed on another device.
|
|
13
|
+
- `<SignIn>` shows "No account? Sign up" on every step (`signUpUrl`; default the hosted sign-up page with the visitor's referral, `null` hides it), so a newcomer on a password-only instance always has a way to create an account.
|
|
14
|
+
|
|
15
|
+
## 0.7.0
|
|
16
|
+
- **Referral attribution.** The prebuilt forms send the referral the visitor arrived with on sign-in and sign-up (through `@atlasauth/js` 0.13.0) when the instance has referrals on, and show the captcha for a referred sign-up when the instance requires it. New init options: `referral?: false` and `referralConsent?`.
|
|
17
|
+
|
|
3
18
|
## 0.6.1
|
|
4
19
|
- **`<UserProfile>` can verify an address.** Each unverified email address now has a **Verify** button: it emails a code (`POST /v1/client/me/email_addresses/{id}/prepare_verification`) and shows a field to enter it (`attempt_verification`), then reloads the user. This includes the sign-up address on an app that does not require email verification, which now reads unverified until it is proven. New catalog keys: `userProfile.verify`, `userProfile.verifyConfirm`, `userProfile.verifyCodePlaceholder`, `userProfile.verifySendFailed`, `userProfile.verifyCodeRejected`.
|
|
5
20
|
|
package/dist/accessors.js
CHANGED
|
@@ -115,11 +115,12 @@ function createFlow() {
|
|
|
115
115
|
return;
|
|
116
116
|
// The ticket is exchanged by a normal request, so no session token has
|
|
117
117
|
// ever appeared in a URL.
|
|
118
|
-
await client.post('/v1/client/tickets/exchange', {
|
|
118
|
+
const res = await client.post('/v1/client/tickets/exchange', {
|
|
119
119
|
attempt_id: next.attempt.id,
|
|
120
120
|
ticket,
|
|
121
121
|
});
|
|
122
|
-
await atlas.reload();
|
|
122
|
+
const kept = res.ok && (await atlas.reload());
|
|
123
|
+
state.update((current) => ({ ...current, sessionKept: kept }));
|
|
123
124
|
})();
|
|
124
125
|
}, 2_000);
|
|
125
126
|
}
|
|
@@ -127,18 +128,24 @@ function createFlow() {
|
|
|
127
128
|
// for cookies over a same-origin request — FAPI is cross-origin to the app
|
|
128
129
|
// and cannot set the cookie on the completion response itself — THEN
|
|
129
130
|
// re-boot so the rest of the app sees a signed-in user.
|
|
131
|
+
// Stays busy until this browser actually holds the session: the completion
|
|
132
|
+
// alone says nothing about whether the cookie was kept.
|
|
130
133
|
async function finish(next) {
|
|
131
|
-
state.set(next);
|
|
132
134
|
if (next.attempt?.status === 'complete') {
|
|
135
|
+
state.set({ ...next, busy: true });
|
|
136
|
+
let exchanged = true;
|
|
133
137
|
if (next.ticket) {
|
|
134
|
-
await client.post('/v1/client/tickets/exchange', {
|
|
138
|
+
const res = await client.post('/v1/client/tickets/exchange', {
|
|
135
139
|
attempt_id: next.attempt.id,
|
|
136
140
|
ticket: next.ticket,
|
|
137
141
|
});
|
|
142
|
+
exchanged = res.ok;
|
|
138
143
|
}
|
|
139
|
-
await atlas.reload();
|
|
144
|
+
const kept = exchanged && (await atlas.reload());
|
|
145
|
+
state.set({ ...next, busy: false, sessionKept: kept });
|
|
140
146
|
return;
|
|
141
147
|
}
|
|
148
|
+
state.set(next);
|
|
142
149
|
maybeStartPolling();
|
|
143
150
|
}
|
|
144
151
|
function submit(values) {
|
|
@@ -11,6 +11,7 @@ import CaptchaSlot from "./CaptchaSlot.svelte";
|
|
|
11
11
|
export let attempt = null;
|
|
12
12
|
export let busy = false;
|
|
13
13
|
export let errors = [];
|
|
14
|
+
export let sessionKept = void 0;
|
|
14
15
|
export let titleKey;
|
|
15
16
|
export let onSubmit;
|
|
16
17
|
export let captcha = null;
|
|
@@ -75,6 +76,8 @@ $: message = (() => {
|
|
|
75
76
|
case "await_oauth":
|
|
76
77
|
return t("signIn.magicLinkSent");
|
|
77
78
|
case "done":
|
|
79
|
+
if (sessionKept === true) return t("signIn.signedIn");
|
|
80
|
+
if (sessionKept === false) return t("signIn.sessionNotKept");
|
|
78
81
|
return t("signIn.checkOtherTab");
|
|
79
82
|
case "restart":
|
|
80
83
|
return t("error.generic");
|
|
@@ -114,7 +117,11 @@ function handleSubmit(event) {
|
|
|
114
117
|
|
|
115
118
|
<form on:submit={handleSubmit}>
|
|
116
119
|
{#if message !== null}
|
|
117
|
-
|
|
120
|
+
{#if step.kind === 'done' && sessionKept === false}
|
|
121
|
+
<p class={cls('formFieldError', 'atlas-error')} role="alert">{message}</p>
|
|
122
|
+
{:else}
|
|
123
|
+
<p>{message}</p>
|
|
124
|
+
{/if}
|
|
118
125
|
{:else}
|
|
119
126
|
{#if step.kind === 'collect_captcha'}
|
|
120
127
|
<!-- §5 the server withheld the attempt until a captcha clears. Solving
|
|
@@ -12,6 +12,7 @@ declare const __propDef: {
|
|
|
12
12
|
*/ attempt?: AttemptView | null;
|
|
13
13
|
busy?: boolean;
|
|
14
14
|
errors?: FieldError[];
|
|
15
|
+
/** `FlowState.sessionKept`: whether this browser kept the session the sign-in created. */ sessionKept?: boolean | undefined;
|
|
15
16
|
titleKey: string;
|
|
16
17
|
onSubmit: (values: Record<string, string>) => void;
|
|
17
18
|
/**
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
<script>import { onDestroy } from "svelte";
|
|
2
|
+
import { signUp as hostedSignUp } from "@atlasauth/js";
|
|
2
3
|
import { signIn } from "../accessors";
|
|
4
|
+
import { useAtlas } from "../context";
|
|
5
|
+
import { classFor } from "../appearance";
|
|
6
|
+
import { translate } from "../i18n";
|
|
3
7
|
import AttemptFlow from "./AttemptFlow.svelte";
|
|
4
8
|
export let afterUrl = void 0;
|
|
9
|
+
export let signUpUrl = void 0;
|
|
10
|
+
const atlas = useAtlas();
|
|
11
|
+
const t = (key) => translate(atlas.catalog, key);
|
|
12
|
+
function signUpHref(url, after) {
|
|
13
|
+
if (url === null) return null;
|
|
14
|
+
if (url) return url;
|
|
15
|
+
try {
|
|
16
|
+
return hostedSignUp({
|
|
17
|
+
frontendApi: atlas.frontendApi,
|
|
18
|
+
redirectUrl: after ?? (typeof window !== "undefined" ? window.location.href : void 0),
|
|
19
|
+
navigate: false
|
|
20
|
+
});
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
$: promptHref = $flowState.attempt?.status !== "complete" ? signUpHref(signUpUrl, afterUrl) : null;
|
|
5
26
|
const flow = signIn();
|
|
6
27
|
const flowState = flow.state;
|
|
7
28
|
const settings = flow.settings;
|
|
@@ -14,9 +35,16 @@ $: captcha = drawn($settings, $flowState);
|
|
|
14
35
|
attempt={$flowState.attempt}
|
|
15
36
|
busy={$flowState.busy}
|
|
16
37
|
errors={$flowState.errors}
|
|
38
|
+
sessionKept={$flowState.sessionKept}
|
|
17
39
|
titleKey="signIn.title"
|
|
18
40
|
onSubmit={flow.submit}
|
|
19
41
|
onPrepare={flow.prepare}
|
|
20
42
|
emailDelivery={$settings?.emailDelivery ?? null}
|
|
21
43
|
{captcha}
|
|
22
44
|
/>
|
|
45
|
+
|
|
46
|
+
{#if promptHref}
|
|
47
|
+
<p class={classFor('footerAction', atlas.appearance, 'atlas-footer')} data-atlas-sign-up-prompt="">
|
|
48
|
+
{t('signIn.noAccount')} <a class="atlas-link" href={promptHref}>{t('signIn.signUpLink')}</a>
|
|
49
|
+
</p>
|
|
50
|
+
{/if}
|
|
@@ -9,6 +9,11 @@ declare const __propDef: {
|
|
|
9
9
|
* Offers emailed first factors (code / link) only while the app can deliver
|
|
10
10
|
* email (`settings.emailDelivery`).
|
|
11
11
|
*/ afterUrl?: string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Where "No account? Sign up" goes. Default: the instance's hosted sign-up
|
|
14
|
+
* page (carrying the visitor's referral). `null` hides it. Always shown, never
|
|
15
|
+
* only after a failed sign-in, so it says nothing about whether an email has an account.
|
|
16
|
+
*/ signUpUrl?: string | null | undefined;
|
|
12
17
|
};
|
|
13
18
|
events: {
|
|
14
19
|
[evt: string]: CustomEvent<any>;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Readable } from 'svelte/store';
|
|
2
|
-
import { type ProviderToken, type SignOutOptions, type SignOutResult, type SignOutEvent, type SignOutListener } from '@atlasauth/js';
|
|
2
|
+
import { type ProviderToken, type SignOutOptions, type SignOutResult, type SignOutEvent, type SignOutListener, type ReferralConsent } from '@atlasauth/js';
|
|
3
3
|
import { type Catalog } from './i18n';
|
|
4
4
|
import { type Appearance, type InstanceDesign } from './appearance';
|
|
5
5
|
import type { AtlasOrganizationMembership, AtlasSession, AtlasUser, SessionClaims } from './types';
|
|
@@ -23,6 +23,18 @@ export type AuthStatus = 'loading' | 'signed_in' | 'signed_out';
|
|
|
23
23
|
export declare const ATLAS_KEY: unique symbol;
|
|
24
24
|
export interface AtlasInitOptions {
|
|
25
25
|
publishableKey: string;
|
|
26
|
+
/**
|
|
27
|
+
* Referral attribution: the prebuilt forms send the referral the visitor
|
|
28
|
+
* arrived with on sign-in / sign-up when the instance has referrals on.
|
|
29
|
+
* `false` turns that off.
|
|
30
|
+
*/
|
|
31
|
+
referral?: false;
|
|
32
|
+
/**
|
|
33
|
+
* The app's own consent decision for the referral tracking cookie, used only
|
|
34
|
+
* when Cookie Munch is not on the page: `'granted'` (or a function returning
|
|
35
|
+
* it) lets the forms read the cookie. Default: the cookie is not read.
|
|
36
|
+
*/
|
|
37
|
+
referralConsent?: ReferralConsent;
|
|
26
38
|
/**
|
|
27
39
|
* The instance's Frontend API: an absolute URL such as
|
|
28
40
|
* `"https://auth.example.com"`, or `"same-origin"` / a path such as `"/atlas"`
|
|
@@ -114,7 +126,8 @@ export interface AtlasClient extends AtlasStores {
|
|
|
114
126
|
*/
|
|
115
127
|
signOut(options?: SignOutOptions): Promise<SignOutResult>;
|
|
116
128
|
setActiveOrganization(organizationId: string | null): Promise<void>;
|
|
117
|
-
|
|
129
|
+
/** Re-boot from the Frontend API. Resolves true when a session now exists in this browser. */
|
|
130
|
+
reload(): Promise<boolean>;
|
|
118
131
|
/**
|
|
119
132
|
* Redirect handling + the first `/v1/client` load. Runs once, in the browser
|
|
120
133
|
* only. {@link initAtlas} calls it automatically unless `manualBoot` is set;
|
package/dist/context.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
import { derived, get, writable } from 'svelte/store';
|
|
3
|
-
import { FapiClient, SignOutGuard, TokenCache, closeOAuthPopupAfterSignIn, completeOAuthPopupLanding, getProviderToken as fetchProviderToken, mapOAuthPopupPayload, msUntilRefresh, readExpiry, readRedirectResult, resolveFrontendApi, stripRedirectParams, catalogLocale, SDK_HEADER, SignOutNotifier, getTemplateToken, reportSdkAdvisory, sdkHeaderValue, signOutReasonFromClient, } from '@atlasauth/js';
|
|
3
|
+
import { FapiClient, SignOutGuard, TokenCache, closeOAuthPopupAfterSignIn, completeOAuthPopupLanding, getProviderToken as fetchProviderToken, mapOAuthPopupPayload, msUntilRefresh, readExpiry, readRedirectResult, resolveFrontendApi, stripRedirectParams, catalogLocale, SDK_HEADER, SignOutNotifier, getTemplateToken, reportSdkAdvisory, sdkHeaderValue, signOutReasonFromClient, configureReferralCapture, withDevBrowser, } from '@atlasauth/js';
|
|
4
4
|
import { ATLAS_SVELTE_SDK } from './version';
|
|
5
5
|
import { EN_US, withFallback } from './i18n';
|
|
6
6
|
import { instanceDesignFrom } from './appearance';
|
|
@@ -28,7 +28,9 @@ export function createAtlasClient(options) {
|
|
|
28
28
|
const baseUrl = resolveFrontendApi(options.frontendApi, {
|
|
29
29
|
component: '<AtlasProvider> / initAtlas()',
|
|
30
30
|
});
|
|
31
|
-
const
|
|
31
|
+
const rawFetch = options.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);
|
|
32
|
+
// Development instances keep the session in a header where the browser blocks the cookie.
|
|
33
|
+
const doFetch = rawFetch ? withDevBrowser(options.publishableKey, rawFetch) : undefined;
|
|
32
34
|
const catalog = options.localization ? withFallback(options.localization) : EN_US;
|
|
33
35
|
const errorCatalog = options.localization ?? EN_US;
|
|
34
36
|
const locale = catalogLocale(options.localization);
|
|
@@ -139,7 +141,7 @@ export function createAtlasClient(options) {
|
|
|
139
141
|
// refused: stay signed out; the guard re-tries the revoke in the background.
|
|
140
142
|
if (!body.session || !body.user || guard.refuse(body.session.id)) {
|
|
141
143
|
clearSession(body.session ? 'other_tab' : signOutReasonFromClient(body));
|
|
142
|
-
return;
|
|
144
|
+
return false;
|
|
143
145
|
}
|
|
144
146
|
notifier.clear();
|
|
145
147
|
user.set(body.user);
|
|
@@ -149,6 +151,7 @@ export function createAtlasClient(options) {
|
|
|
149
151
|
applyToken(body.jwt, body.session.id);
|
|
150
152
|
status.set('signed_in');
|
|
151
153
|
scheduleRefresh();
|
|
154
|
+
return true;
|
|
152
155
|
}
|
|
153
156
|
catch {
|
|
154
157
|
/**
|
|
@@ -157,6 +160,7 @@ export function createAtlasClient(options) {
|
|
|
157
160
|
* re-authenticate for no reason.
|
|
158
161
|
*/
|
|
159
162
|
status.update((current) => (current === 'loading' ? 'signed_out' : current));
|
|
163
|
+
return false;
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
async function refresh() {
|
|
@@ -191,6 +195,8 @@ export function createAtlasClient(options) {
|
|
|
191
195
|
await refresh();
|
|
192
196
|
return cache.get(5_000)?.jwt ?? null;
|
|
193
197
|
}
|
|
198
|
+
// Referral attribution: app-wide options every FAPI client on the page falls back to.
|
|
199
|
+
configureReferralCapture({ referral: options.referral, referralConsent: options.referralConsent });
|
|
194
200
|
const fapiClient = new FapiClient({ publishableKey, baseUrl, fetchImpl: doFetch, locale, sdk: ATLAS_SVELTE_SDK });
|
|
195
201
|
function getProviderToken(provider) {
|
|
196
202
|
return fetchProviderToken(fapiClient, provider);
|
package/dist/i18n.js
CHANGED
|
@@ -31,6 +31,8 @@ export const EN_US = {
|
|
|
31
31
|
'signIn.resendCode': 'Resend code',
|
|
32
32
|
'signIn.resendLink': 'Resend link',
|
|
33
33
|
'signIn.passwordOption': 'Use your password instead',
|
|
34
|
+
'signIn.signedIn': 'You\'re signed in.',
|
|
35
|
+
'signIn.sessionNotKept': 'You signed in, but this browser didn\'t keep the session. If it blocks third-party cookies, allow cookies for this site and try again.',
|
|
34
36
|
'signIn.checkOtherTab': 'You can close this tab — you are signed in where you started.',
|
|
35
37
|
'signUp.title': 'Create your account',
|
|
36
38
|
'signUp.submit': 'Continue',
|
package/dist/version.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AtlasSdkInfo } from '@atlasauth/js';
|
|
2
2
|
/** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
|
|
3
|
-
export declare const ATLAS_SVELTE_VERSION = "0.
|
|
3
|
+
export declare const ATLAS_SVELTE_VERSION = "0.8.0";
|
|
4
4
|
export declare const ATLAS_SVELTE_SDK: AtlasSdkInfo;
|
package/dist/version.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
|
|
2
|
-
export const ATLAS_SVELTE_VERSION = '0.
|
|
2
|
+
export const ATLAS_SVELTE_VERSION = '0.8.0';
|
|
3
3
|
export const ATLAS_SVELTE_SDK = { name: '@atlasauth/svelte', version: ATLAS_SVELTE_VERSION };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"svelte": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@atlasauth/design": "0.2.0",
|
|
26
|
-
"@atlasauth/
|
|
26
|
+
"@atlasauth/backend": "0.14.0",
|
|
27
27
|
"@atlasauth/authz": "0.1.1",
|
|
28
|
-
"@atlasauth/
|
|
28
|
+
"@atlasauth/js": "0.15.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"svelte": ">=4",
|