@atlasauth/solid 0.0.0-stage → 0.1.1
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/README.md +54 -2
- package/dist/accessors.d.ts +45 -0
- package/dist/accessors.js +46 -0
- package/dist/accessors.js.map +1 -0
- package/dist/context.d.ts +19 -0
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -0
- package/dist/control-components.d.ts +51 -0
- package/dist/control-components.js +80 -0
- package/dist/control-components.js.map +1 -0
- package/dist/h.d.ts +13 -0
- package/dist/h.js +14 -0
- package/dist/h.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +100 -0
- package/dist/provider.js +239 -0
- package/dist/provider.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/widgets.d.ts +8 -0
- package/dist/widgets.js +207 -0
- package/dist/widgets.js.map +1 -0
- package/package.json +44 -3
package/README.md
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/solid
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
SolidJS bindings for [Atlas](https://atlasauth.net). Wraps the
|
|
4
|
+
framework-agnostic `@atlasauth/js` client in Solid's fine-grained reactivity:
|
|
5
|
+
drop in `<AtlasProvider>`, read auth state through accessors, and gate UI with
|
|
6
|
+
control components — the Solid peer of `@atlasauth/react` and `@atlasauth/vue`.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
AtlasProvider,
|
|
11
|
+
SignedIn,
|
|
12
|
+
SignedOut,
|
|
13
|
+
SignIn,
|
|
14
|
+
UserButton,
|
|
15
|
+
useAuth,
|
|
16
|
+
} from '@atlasauth/solid';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<AtlasProvider publishableKey="pk_test_...">
|
|
21
|
+
<SignedIn>
|
|
22
|
+
<UserButton />
|
|
23
|
+
</SignedIn>
|
|
24
|
+
<SignedOut>
|
|
25
|
+
<SignIn />
|
|
26
|
+
</SignedOut>
|
|
27
|
+
</AtlasProvider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Reactive accessors
|
|
33
|
+
|
|
34
|
+
Each returns Solid accessors (`() => T`), so a component re-runs only when the
|
|
35
|
+
field it reads changes:
|
|
36
|
+
|
|
37
|
+
- `useAtlas()` — the underlying client.
|
|
38
|
+
- `useAuth()` — `{ isLoaded, isSignedIn, userId, sessionId, orgId, orgRole, getToken, getProviderToken, signOut, has }`.
|
|
39
|
+
- `useUser()` — `{ isLoaded, isSignedIn, user, getProviderToken }`.
|
|
40
|
+
- `useSession()` — `{ isLoaded, session }`.
|
|
41
|
+
|
|
42
|
+
## Control components
|
|
43
|
+
|
|
44
|
+
`<SignedIn>`, `<SignedOut>`, `<AtlasLoading>`, `<AtlasLoaded>`, and
|
|
45
|
+
`<Protect permission="…" fallback={…}>` — all `<Show>`-based. `<Protect>` is a
|
|
46
|
+
rendering helper, never an authorization boundary: the server checking the same
|
|
47
|
+
permission is what actually stops an action.
|
|
48
|
+
|
|
49
|
+
## Widgets
|
|
50
|
+
|
|
51
|
+
`<SignIn>`, `<SignUp>`, and `<UserButton>` mount Atlas's server-driven flow into
|
|
52
|
+
a host element on mount and tear it down on cleanup.
|
|
53
|
+
|
|
54
|
+
> SSR-safe: the provider boots only in the browser; on the server it renders a
|
|
55
|
+
> `loading` state and hydrates into the real status on the client.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ProtectCondition, type ProviderToken } from '@atlasauth/js';
|
|
2
|
+
import type { AuthStatus } from './provider';
|
|
3
|
+
import type { AtlasSession, AtlasUser } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* The Solid peer of React's hooks and Vue's composables.
|
|
6
|
+
*
|
|
7
|
+
* Every one returns `isLoaded` alongside its data, and that is not ceremony:
|
|
8
|
+
* without it a component cannot tell "still booting" from "signed out", so it
|
|
9
|
+
* flashes a sign-in form for a frame on every load — the flash of
|
|
10
|
+
* unauthenticated content that makes an app feel broken. Each returned value is
|
|
11
|
+
* an ACCESSOR (a `() => T` function), the idiomatic Solid shape: call it inside
|
|
12
|
+
* JSX or an effect and that spot re-runs when just that field changes.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useUser(): {
|
|
15
|
+
isLoaded: () => boolean;
|
|
16
|
+
isSignedIn: () => boolean;
|
|
17
|
+
user: () => AtlasUser | null;
|
|
18
|
+
/**
|
|
19
|
+
* The signed-in user's own live token at a connected provider — always the
|
|
20
|
+
* caller's (the server keys off the session), refreshed on read if stale, and
|
|
21
|
+
* never the refresh token.
|
|
22
|
+
*/
|
|
23
|
+
getProviderToken: (provider: string) => Promise<ProviderToken | null>;
|
|
24
|
+
};
|
|
25
|
+
export declare function useSession(): {
|
|
26
|
+
isLoaded: () => boolean;
|
|
27
|
+
session: () => AtlasSession | null;
|
|
28
|
+
};
|
|
29
|
+
export declare function useAuth(): {
|
|
30
|
+
isLoaded: () => boolean;
|
|
31
|
+
isSignedIn: () => boolean;
|
|
32
|
+
/** Three-state auth status (`loading` | `signed_in` | `signed_out`); same source as isLoaded/isSignedIn. */
|
|
33
|
+
status: () => AuthStatus;
|
|
34
|
+
/** The active session, or null while loading or signed out. */
|
|
35
|
+
session: () => AtlasSession | null;
|
|
36
|
+
userId: () => string | null;
|
|
37
|
+
sessionId: () => string | null;
|
|
38
|
+
orgId: () => string | null;
|
|
39
|
+
orgRole: () => string | null;
|
|
40
|
+
getToken: () => Promise<string | null>;
|
|
41
|
+
getProviderToken: (provider: string) => Promise<ProviderToken | null>;
|
|
42
|
+
signOut: () => Promise<void>;
|
|
43
|
+
/** Mirrors `<Protect>`, for logic that is not a render decision. */
|
|
44
|
+
has: (condition: ProtectCondition) => boolean;
|
|
45
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { evaluate } from '@atlasauth/js';
|
|
2
|
+
import { useAtlas } from './context';
|
|
3
|
+
/**
|
|
4
|
+
* The Solid peer of React's hooks and Vue's composables.
|
|
5
|
+
*
|
|
6
|
+
* Every one returns `isLoaded` alongside its data, and that is not ceremony:
|
|
7
|
+
* without it a component cannot tell "still booting" from "signed out", so it
|
|
8
|
+
* flashes a sign-in form for a frame on every load — the flash of
|
|
9
|
+
* unauthenticated content that makes an app feel broken. Each returned value is
|
|
10
|
+
* an ACCESSOR (a `() => T` function), the idiomatic Solid shape: call it inside
|
|
11
|
+
* JSX or an effect and that spot re-runs when just that field changes.
|
|
12
|
+
*/
|
|
13
|
+
export function useUser() {
|
|
14
|
+
const client = useAtlas();
|
|
15
|
+
return {
|
|
16
|
+
isLoaded: () => client.state.status !== 'loading',
|
|
17
|
+
isSignedIn: () => client.state.status === 'signed_in',
|
|
18
|
+
user: () => client.state.user,
|
|
19
|
+
getProviderToken: client.getProviderToken,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function useSession() {
|
|
23
|
+
const client = useAtlas();
|
|
24
|
+
return {
|
|
25
|
+
isLoaded: () => client.state.status !== 'loading',
|
|
26
|
+
session: () => client.state.session,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function useAuth() {
|
|
30
|
+
const client = useAtlas();
|
|
31
|
+
return {
|
|
32
|
+
isLoaded: () => client.state.status !== 'loading',
|
|
33
|
+
isSignedIn: () => client.state.status === 'signed_in',
|
|
34
|
+
status: () => client.state.status,
|
|
35
|
+
session: () => client.state.session,
|
|
36
|
+
userId: () => client.state.claims?.sub ?? null,
|
|
37
|
+
sessionId: () => client.state.claims?.sid ?? null,
|
|
38
|
+
orgId: () => client.state.claims?.org_id ?? null,
|
|
39
|
+
orgRole: () => client.state.claims?.org_role ?? null,
|
|
40
|
+
getToken: client.getToken,
|
|
41
|
+
getProviderToken: client.getProviderToken,
|
|
42
|
+
signOut: client.signOut,
|
|
43
|
+
has: (condition) => evaluate(client.state.claims, condition).allowed,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=accessors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessors.js","sourceRoot":"","sources":["../src/accessors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA6C,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAIrC;;;;;;;;;GASG;AAEH,MAAM,UAAU,OAAO;IAWrB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;QACjD,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,WAAW;QACrD,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI;QAC7B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;KAC1C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IAIxB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;QACjD,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO;IAiBrB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;QACjD,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,WAAW;QACrD,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;QACjC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI;QAC9C,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI;QACjD,KAAK,EAAE,GAAG,EAAE,CAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAA6B,IAAI,IAAI;QACxE,OAAO,EAAE,GAAG,EAAE,CAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAA+B,IAAI,IAAI;QAC5E,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,GAAG,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,OAAO;KACrE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AtlasClient } from './provider';
|
|
2
|
+
/**
|
|
3
|
+
* The injected Atlas client — the Solid peer of React's `AtlasContext` and Vue's
|
|
4
|
+
* `ATLAS_INJECTION_KEY`.
|
|
5
|
+
*
|
|
6
|
+
* `<AtlasProvider>` puts exactly one `AtlasClient` here, so every accessor and
|
|
7
|
+
* component reaches a single reactive source of auth truth per provider — never
|
|
8
|
+
* a module-level singleton that would leak one request's session into another
|
|
9
|
+
* on an SSR server sharing a process.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AtlasContext: import("solid-js").Context<AtlasClient | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Reach the Atlas client from inside the provider tree.
|
|
14
|
+
*
|
|
15
|
+
* Throws rather than handing back a null-ish default. An accessor that returned
|
|
16
|
+
* `{ user: null }` outside the provider is indistinguishable from a signed-out
|
|
17
|
+
* user, and the developer loses an afternoon to it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useAtlas(): AtlasClient;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createContext, useContext } from 'solid-js';
|
|
2
|
+
/**
|
|
3
|
+
* The injected Atlas client — the Solid peer of React's `AtlasContext` and Vue's
|
|
4
|
+
* `ATLAS_INJECTION_KEY`.
|
|
5
|
+
*
|
|
6
|
+
* `<AtlasProvider>` puts exactly one `AtlasClient` here, so every accessor and
|
|
7
|
+
* component reaches a single reactive source of auth truth per provider — never
|
|
8
|
+
* a module-level singleton that would leak one request's session into another
|
|
9
|
+
* on an SSR server sharing a process.
|
|
10
|
+
*/
|
|
11
|
+
export const AtlasContext = createContext();
|
|
12
|
+
/**
|
|
13
|
+
* Reach the Atlas client from inside the provider tree.
|
|
14
|
+
*
|
|
15
|
+
* Throws rather than handing back a null-ish default. An accessor that returned
|
|
16
|
+
* `{ user: null }` outside the provider is indistinguishable from a signed-out
|
|
17
|
+
* user, and the developer loses an afternoon to it.
|
|
18
|
+
*/
|
|
19
|
+
export function useAtlas() {
|
|
20
|
+
const client = useContext(AtlasContext);
|
|
21
|
+
if (!client) {
|
|
22
|
+
throw new Error('Atlas accessors and components must be used inside <AtlasProvider publishableKey="...">.');
|
|
23
|
+
}
|
|
24
|
+
return client;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,EAAe,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type JSX } from 'solid-js';
|
|
2
|
+
import { type ProtectCondition } from '@atlasauth/js';
|
|
3
|
+
/**
|
|
4
|
+
* Control / conditional-rendering components — the Solid peers of React's
|
|
5
|
+
* `control-components.tsx` and Vue's `<SignedIn>` / `<Protect>`.
|
|
6
|
+
*
|
|
7
|
+
* All three are thin `<Show>` wrappers. `when` is passed as a zero-arg FUNCTION
|
|
8
|
+
* (an accessor), which `solid-js/h` converts to a reactive component prop — so
|
|
9
|
+
* the branch flips when auth state changes without re-rendering anything above
|
|
10
|
+
* it. (A plain getter would NOT be made reactive by `h`; only a function value
|
|
11
|
+
* is.) Children are a function so `<Show>` realises them only on the winning
|
|
12
|
+
* branch.
|
|
13
|
+
*/
|
|
14
|
+
export declare function SignedIn(props: {
|
|
15
|
+
children?: JSX.Element;
|
|
16
|
+
}): JSX.Element;
|
|
17
|
+
export declare function SignedOut(props: {
|
|
18
|
+
children?: JSX.Element;
|
|
19
|
+
}): JSX.Element;
|
|
20
|
+
/** Render children only while the SDK is still loading. */
|
|
21
|
+
export declare function AtlasLoading(props: {
|
|
22
|
+
children?: JSX.Element;
|
|
23
|
+
}): JSX.Element;
|
|
24
|
+
/** Render children only once the SDK has finished loading — the symmetric
|
|
25
|
+
* partner of {@link AtlasLoading}. */
|
|
26
|
+
export declare function AtlasLoaded(props: {
|
|
27
|
+
children?: JSX.Element;
|
|
28
|
+
}): JSX.Element;
|
|
29
|
+
export type ProtectProps = ProtectCondition & {
|
|
30
|
+
children?: JSX.Element;
|
|
31
|
+
fallback?: JSX.Element;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* `<Protect permission="…">` — conditional rendering.
|
|
35
|
+
*
|
|
36
|
+
* A rendering helper, NEVER an authorization boundary — anyone can flip the
|
|
37
|
+
* condition in devtools, and seeing is not doing. The server checking the same
|
|
38
|
+
* permission is the only thing that actually stops an action; this component's
|
|
39
|
+
* job is just to avoid showing a button that would fail. The evaluation lives in
|
|
40
|
+
* `@atlasauth/authz` (re-exported by `@atlasauth/js`), the one primitive every
|
|
41
|
+
* SDK shares, so this `<Protect>` and the server's `has()` judge a condition
|
|
42
|
+
* identically.
|
|
43
|
+
*
|
|
44
|
+
* Implemented as a `createMemo` whose value IS the content to render, returned
|
|
45
|
+
* straight from the component (not wrapped in `<Show>`). A component that
|
|
46
|
+
* returns another component — `<Protect>` returning `<Show>` — breaks under
|
|
47
|
+
* `solid-js/h`: the returned component's reactive scope stops updating, so the
|
|
48
|
+
* gate never reopened once the token's claims landed after boot. Returning a
|
|
49
|
+
* reactive accessor is how a function child behaves, and that tracks correctly.
|
|
50
|
+
*/
|
|
51
|
+
export declare function Protect(props: ProtectProps): JSX.Element;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Show, createMemo } from 'solid-js';
|
|
2
|
+
import { h } from './h';
|
|
3
|
+
import { evaluate } from '@atlasauth/js';
|
|
4
|
+
import { useAtlas } from './context';
|
|
5
|
+
import { useUser } from './accessors';
|
|
6
|
+
/**
|
|
7
|
+
* Control / conditional-rendering components — the Solid peers of React's
|
|
8
|
+
* `control-components.tsx` and Vue's `<SignedIn>` / `<Protect>`.
|
|
9
|
+
*
|
|
10
|
+
* All three are thin `<Show>` wrappers. `when` is passed as a zero-arg FUNCTION
|
|
11
|
+
* (an accessor), which `solid-js/h` converts to a reactive component prop — so
|
|
12
|
+
* the branch flips when auth state changes without re-rendering anything above
|
|
13
|
+
* it. (A plain getter would NOT be made reactive by `h`; only a function value
|
|
14
|
+
* is.) Children are a function so `<Show>` realises them only on the winning
|
|
15
|
+
* branch.
|
|
16
|
+
*/
|
|
17
|
+
export function SignedIn(props) {
|
|
18
|
+
const { isLoaded, isSignedIn } = useUser();
|
|
19
|
+
// Renders nothing while loading rather than flashing the signed-out branch.
|
|
20
|
+
return h(Show, { when: () => isLoaded() && isSignedIn() }, () => props.children);
|
|
21
|
+
}
|
|
22
|
+
export function SignedOut(props) {
|
|
23
|
+
const { isLoaded, isSignedIn } = useUser();
|
|
24
|
+
return h(Show, { when: () => isLoaded() && !isSignedIn() }, () => props.children);
|
|
25
|
+
}
|
|
26
|
+
/** Render children only while the SDK is still loading. */
|
|
27
|
+
export function AtlasLoading(props) {
|
|
28
|
+
const { isLoaded } = useUser();
|
|
29
|
+
return h(Show, { when: () => !isLoaded() }, () => props.children);
|
|
30
|
+
}
|
|
31
|
+
/** Render children only once the SDK has finished loading — the symmetric
|
|
32
|
+
* partner of {@link AtlasLoading}. */
|
|
33
|
+
export function AtlasLoaded(props) {
|
|
34
|
+
const { isLoaded } = useUser();
|
|
35
|
+
return h(Show, { when: () => isLoaded() }, () => props.children);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `<Protect permission="…">` — conditional rendering.
|
|
39
|
+
*
|
|
40
|
+
* A rendering helper, NEVER an authorization boundary — anyone can flip the
|
|
41
|
+
* condition in devtools, and seeing is not doing. The server checking the same
|
|
42
|
+
* permission is the only thing that actually stops an action; this component's
|
|
43
|
+
* job is just to avoid showing a button that would fail. The evaluation lives in
|
|
44
|
+
* `@atlasauth/authz` (re-exported by `@atlasauth/js`), the one primitive every
|
|
45
|
+
* SDK shares, so this `<Protect>` and the server's `has()` judge a condition
|
|
46
|
+
* identically.
|
|
47
|
+
*
|
|
48
|
+
* Implemented as a `createMemo` whose value IS the content to render, returned
|
|
49
|
+
* straight from the component (not wrapped in `<Show>`). A component that
|
|
50
|
+
* returns another component — `<Protect>` returning `<Show>` — breaks under
|
|
51
|
+
* `solid-js/h`: the returned component's reactive scope stops updating, so the
|
|
52
|
+
* gate never reopened once the token's claims landed after boot. Returning a
|
|
53
|
+
* reactive accessor is how a function child behaves, and that tracks correctly.
|
|
54
|
+
*/
|
|
55
|
+
export function Protect(props) {
|
|
56
|
+
const client = useAtlas();
|
|
57
|
+
const loaded = () => client.state.status !== 'loading';
|
|
58
|
+
// Capture the condition ONCE as a plain object; it is fixed for the
|
|
59
|
+
// component's life (the `permission`/`role` props do not change the way auth
|
|
60
|
+
// state does).
|
|
61
|
+
const condition = {
|
|
62
|
+
permission: props.permission,
|
|
63
|
+
role: props.role,
|
|
64
|
+
anyPermission: props.anyPermission,
|
|
65
|
+
allPermissions: props.allPermissions,
|
|
66
|
+
plan: props.plan,
|
|
67
|
+
feature: props.feature,
|
|
68
|
+
};
|
|
69
|
+
// Read status AND claims unconditionally on every run so both are tracked
|
|
70
|
+
// from the first evaluation — while loading, show nothing (not the fallback);
|
|
71
|
+
// once loaded, show children when allowed and the caller's fallback when not.
|
|
72
|
+
const content = createMemo(() => {
|
|
73
|
+
const claims = client.state.claims;
|
|
74
|
+
if (!loaded())
|
|
75
|
+
return null;
|
|
76
|
+
return evaluate(claims, condition).allowed ? props.children : (props.fallback ?? null);
|
|
77
|
+
});
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=control-components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-components.js","sourceRoot":"","sources":["../src/control-components.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAY,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAyB,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC;;;;;;;;;;GAUG;AAEH,MAAM,UAAU,QAAQ,CAAC,KAAiC;IACxD,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAAC;IAC3C,4EAA4E;IAC5E,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAiC;IACzD,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAAC;IAC3C,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,YAAY,CAAC,KAAiC;IAC5D,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IAC/B,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACpE,CAAC;AAED;sCACsC;AACtC,MAAM,UAAU,WAAW,CAAC,KAAiC;IAC3D,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IAC/B,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACnE,CAAC;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,OAAO,CAAC,KAAmB;IACzC,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC;IACvD,oEAAoE;IACpE,6EAA6E;IAC7E,eAAe;IACf,MAAM,SAAS,GAAG;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC;IAEF,0EAA0E;IAC1E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;QAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,IAAI,CAAC;QAC3B,OAAO,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,OAAO,OAAiC,CAAC;AAC3C,CAAC"}
|
package/dist/h.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
/**
|
|
3
|
+
* `solid-js/h` is the runtime hyperscript — it lets this package author
|
|
4
|
+
* components WITHOUT JSX, so the build is a plain `tsc` (no Babel/vite-plugin-
|
|
5
|
+
* solid transform) exactly like `@atlasauth/vue`'s `h()` render functions.
|
|
6
|
+
*
|
|
7
|
+
* Its published types declare the output as a thunk (`() => Node`), which is how
|
|
8
|
+
* Solid carries a reactive node. At runtime Solid unwraps that function
|
|
9
|
+
* everywhere it accepts children — `insert`, `render`, `createComponent` — so it
|
|
10
|
+
* IS a valid reactive `JSX.Element`. We retype `h` to say so, which keeps every
|
|
11
|
+
* component signature returning an honest `JSX.Element` instead of a thunk.
|
|
12
|
+
*/
|
|
13
|
+
export declare const h: (type: unknown, props?: unknown, ...children: unknown[]) => JSX.Element;
|
package/dist/h.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import rawH from 'solid-js/h';
|
|
2
|
+
/**
|
|
3
|
+
* `solid-js/h` is the runtime hyperscript — it lets this package author
|
|
4
|
+
* components WITHOUT JSX, so the build is a plain `tsc` (no Babel/vite-plugin-
|
|
5
|
+
* solid transform) exactly like `@atlasauth/vue`'s `h()` render functions.
|
|
6
|
+
*
|
|
7
|
+
* Its published types declare the output as a thunk (`() => Node`), which is how
|
|
8
|
+
* Solid carries a reactive node. At runtime Solid unwraps that function
|
|
9
|
+
* everywhere it accepts children — `insert`, `render`, `createComponent` — so it
|
|
10
|
+
* IS a valid reactive `JSX.Element`. We retype `h` to say so, which keeps every
|
|
11
|
+
* component signature returning an honest `JSX.Element` instead of a thunk.
|
|
12
|
+
*/
|
|
13
|
+
export const h = rawH;
|
|
14
|
+
//# sourceMappingURL=h.js.map
|
package/dist/h.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h.js","sourceRoot":"","sources":["../src/h.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,YAAY,CAAC;AAG9B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,CAAC,GAAG,IAID,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { type JSX } from 'solid-js';
|
|
2
|
+
import { type ProviderToken } from '@atlasauth/js';
|
|
3
|
+
import type { AtlasOrganizationMembership, AtlasSession, AtlasUser, SessionClaims } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* The Solid peer of React's `<AtlasProvider>` and Vue's `createAtlas()` plugin.
|
|
6
|
+
*
|
|
7
|
+
* It holds exactly one thing that matters in memory: the token. Not
|
|
8
|
+
* localStorage, not a cookie this code writes — the refresh token is HttpOnly so
|
|
9
|
+
* browser script never touches it, and the short-lived JWT lives only as long as
|
|
10
|
+
* the tab. A client that persisted either would outlive the tab and become the
|
|
11
|
+
* first thing an XSS reads.
|
|
12
|
+
*/
|
|
13
|
+
export type AuthStatus = 'loading' | 'signed_in' | 'signed_out';
|
|
14
|
+
/**
|
|
15
|
+
* The reactive auth state. A Solid store, so a component that reads
|
|
16
|
+
* `client.state.user` re-runs ONLY when the user changes — the fine-grained
|
|
17
|
+
* peer of Vue's refs, not React's whole-component re-render.
|
|
18
|
+
*/
|
|
19
|
+
export interface AtlasState {
|
|
20
|
+
status: AuthStatus;
|
|
21
|
+
user: AtlasUser | null;
|
|
22
|
+
session: AtlasSession | null;
|
|
23
|
+
claims: SessionClaims | null;
|
|
24
|
+
memberships: AtlasOrganizationMembership[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The client provided to the whole app. Every field React's `AtlasContextValue`
|
|
28
|
+
* exposes, but reactivity is a Solid store (`state`) plus the mount seams the
|
|
29
|
+
* widgets drive, so the public surface stays recognisable across SDKs.
|
|
30
|
+
*/
|
|
31
|
+
export interface AtlasClient {
|
|
32
|
+
publishableKey: string;
|
|
33
|
+
/** The instance's FAPI origin. */
|
|
34
|
+
frontendApi: string;
|
|
35
|
+
/** The reactive auth state; read fields off it inside a tracking scope. */
|
|
36
|
+
state: AtlasState;
|
|
37
|
+
/**
|
|
38
|
+
* A sign-in attempt handed back by an OAuth redirect that still owes a second
|
|
39
|
+
* factor (no session was issued). Null in the ordinary case. `<SignIn>` seeds
|
|
40
|
+
* its flow from this so the user resumes at the 2FA step instead of starting
|
|
41
|
+
* over. Captured synchronously at construction — before any component reads
|
|
42
|
+
* it — because such a redirect carries a status but no ticket.
|
|
43
|
+
*/
|
|
44
|
+
pendingAttempt: {
|
|
45
|
+
id: string;
|
|
46
|
+
status: string;
|
|
47
|
+
} | null;
|
|
48
|
+
/** Always fresh: refreshes first if the cached token is close to expiry. */
|
|
49
|
+
getToken(): Promise<string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* The signed-in user's OWN live token at a connected provider (Google,
|
|
52
|
+
* GitHub, …), so the app can call that provider's API. Null when there is no
|
|
53
|
+
* usable token. The server refreshes a stale one on read; the refresh token
|
|
54
|
+
* is never exposed.
|
|
55
|
+
*/
|
|
56
|
+
getProviderToken(provider: string): Promise<ProviderToken | null>;
|
|
57
|
+
signOut(): Promise<void>;
|
|
58
|
+
setActiveOrganization(organizationId: string | null): Promise<void>;
|
|
59
|
+
reload(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Mount the server-driven sign-in / sign-up flow, or the user button, into a
|
|
62
|
+
* host element and return a disposer. These are the seams `<SignIn>`,
|
|
63
|
+
* `<SignUp>` and `<UserButton>` call in `onMount`, and tear down in
|
|
64
|
+
* `onCleanup`. The widgets live on the client (not in `@atlasauth/js`, which
|
|
65
|
+
* ships no DOM widgets) so a mounted flow can reach this same client — its
|
|
66
|
+
* `reload()` after completion, its `signOut()` — without re-threading context.
|
|
67
|
+
*/
|
|
68
|
+
mountSignIn(el: HTMLElement): () => void;
|
|
69
|
+
mountSignUp(el: HTMLElement): () => void;
|
|
70
|
+
mountUserButton(el: HTMLElement): () => void;
|
|
71
|
+
}
|
|
72
|
+
export interface AtlasProviderProps {
|
|
73
|
+
publishableKey: string;
|
|
74
|
+
/** The instance's FAPI origin. Empty for same-origin. */
|
|
75
|
+
frontendApi?: string;
|
|
76
|
+
/** Injected in tests to drive the client through a mocked network. */
|
|
77
|
+
fetchImpl?: typeof fetch;
|
|
78
|
+
children?: JSX.Element;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build the reactive client. Separated from the component so a test can
|
|
82
|
+
* construct it directly and so the mount seams close over one consistent
|
|
83
|
+
* client.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createAtlasClient(options: {
|
|
86
|
+
publishableKey: string;
|
|
87
|
+
frontendApi?: string;
|
|
88
|
+
fetchImpl?: typeof fetch;
|
|
89
|
+
}): {
|
|
90
|
+
client: AtlasClient;
|
|
91
|
+
boot: () => Promise<void>;
|
|
92
|
+
dispose: () => void;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The provider component. Builds the client, provides it, and boots the session
|
|
96
|
+
* on mount — guarded to the browser: on the server there is no redirect to
|
|
97
|
+
* complete and no cookie jar to read, so the app renders `loading` and hydrates
|
|
98
|
+
* into the real status on the client. The refresh timer is cleared on unmount.
|
|
99
|
+
*/
|
|
100
|
+
export declare function AtlasProvider(props: AtlasProviderProps): JSX.Element;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { onCleanup, onMount } from 'solid-js';
|
|
2
|
+
import { createStore } from 'solid-js/store';
|
|
3
|
+
import { isServer } from 'solid-js/web';
|
|
4
|
+
import { h } from './h';
|
|
5
|
+
import { FapiClient, TokenCache, getProviderToken as fetchProviderToken, msUntilRefresh, readExpiry, readRedirectResult, stripRedirectParams, } from '@atlasauth/js';
|
|
6
|
+
import { AtlasContext } from './context';
|
|
7
|
+
import { mountSignInWidget, mountSignUpWidget, mountUserButtonWidget } from './widgets';
|
|
8
|
+
/**
|
|
9
|
+
* Build the reactive client. Separated from the component so a test can
|
|
10
|
+
* construct it directly and so the mount seams close over one consistent
|
|
11
|
+
* client.
|
|
12
|
+
*/
|
|
13
|
+
export function createAtlasClient(options) {
|
|
14
|
+
const publishableKey = options.publishableKey;
|
|
15
|
+
const baseUrl = options.frontendApi ?? '';
|
|
16
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
17
|
+
const [state, setState] = createStore({
|
|
18
|
+
status: 'loading',
|
|
19
|
+
user: null,
|
|
20
|
+
session: null,
|
|
21
|
+
claims: null,
|
|
22
|
+
memberships: [],
|
|
23
|
+
});
|
|
24
|
+
const cache = new TokenCache();
|
|
25
|
+
let timer = null;
|
|
26
|
+
// A `needs_second_factor` redirect carries a status but no ticket; that is
|
|
27
|
+
// what the 2FA resume seeds from. Read once, synchronously, at construction.
|
|
28
|
+
const pendingAttempt = (() => {
|
|
29
|
+
if (isServer || typeof window === 'undefined')
|
|
30
|
+
return null;
|
|
31
|
+
const result = readRedirectResult(window.location.search);
|
|
32
|
+
return result?.status && !result.ticket
|
|
33
|
+
? { id: result.attemptId, status: result.status }
|
|
34
|
+
: null;
|
|
35
|
+
})();
|
|
36
|
+
const call = (path, init) => doFetch(`${baseUrl}${path}`, {
|
|
37
|
+
...init,
|
|
38
|
+
// Cookies carry the session; a fetch without this is indistinguishable
|
|
39
|
+
// from being signed out.
|
|
40
|
+
credentials: 'include',
|
|
41
|
+
headers: {
|
|
42
|
+
'x-publishable-key': publishableKey,
|
|
43
|
+
...(init?.body ? { 'content-type': 'application/json' } : {}),
|
|
44
|
+
...init?.headers,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const applyToken = (jwt, sessionId) => {
|
|
48
|
+
const expiresAt = readExpiry(jwt);
|
|
49
|
+
if (!expiresAt)
|
|
50
|
+
return;
|
|
51
|
+
cache.set({ jwt, expiresAt, sessionId });
|
|
52
|
+
setState('claims', decodeClaims(jwt));
|
|
53
|
+
};
|
|
54
|
+
// Keep the proactive refresh timer in step with the live token. Called after
|
|
55
|
+
// every status/token change rather than from a reactive effect, so the client
|
|
56
|
+
// behaves identically whether or not it was built inside a Solid root.
|
|
57
|
+
const scheduleRefresh = () => {
|
|
58
|
+
if (timer) {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
timer = null;
|
|
61
|
+
}
|
|
62
|
+
if (state.status !== 'signed_in')
|
|
63
|
+
return;
|
|
64
|
+
const token = cache.peek();
|
|
65
|
+
if (!token)
|
|
66
|
+
return;
|
|
67
|
+
const delay = msUntilRefresh({ token, now: Date.now() });
|
|
68
|
+
timer = setTimeout(() => void refresh(), delay);
|
|
69
|
+
};
|
|
70
|
+
const reload = async () => {
|
|
71
|
+
try {
|
|
72
|
+
const response = await call('/v1/client');
|
|
73
|
+
if (!response.ok)
|
|
74
|
+
throw new Error('boot failed');
|
|
75
|
+
const body = (await response.json());
|
|
76
|
+
if (!body.session || !body.user) {
|
|
77
|
+
cache.clear();
|
|
78
|
+
setState({ status: 'signed_out', user: null, session: null, claims: null, memberships: [] });
|
|
79
|
+
scheduleRefresh();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
setState({
|
|
83
|
+
status: 'signed_in',
|
|
84
|
+
user: body.user,
|
|
85
|
+
session: body.session,
|
|
86
|
+
memberships: body.organization_memberships ?? [],
|
|
87
|
+
});
|
|
88
|
+
if (body.jwt)
|
|
89
|
+
applyToken(body.jwt, body.session.id);
|
|
90
|
+
scheduleRefresh();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// A failed boot is NOT signed-out. Rendering a sign-in form because the
|
|
94
|
+
// network hiccuped throws away a perfectly good session and forces a
|
|
95
|
+
// needless re-auth.
|
|
96
|
+
if (state.status === 'loading')
|
|
97
|
+
setState('status', 'signed_out');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const refresh = async () => {
|
|
101
|
+
const current = cache.peek();
|
|
102
|
+
if (!current)
|
|
103
|
+
return;
|
|
104
|
+
try {
|
|
105
|
+
const response = await call(`/v1/client/sessions/${current.sessionId}/tokens`, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
});
|
|
108
|
+
if (!response.ok)
|
|
109
|
+
throw new Error('refresh failed');
|
|
110
|
+
const body = (await response.json());
|
|
111
|
+
applyToken(body.jwt, body.session_id);
|
|
112
|
+
scheduleRefresh();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// The session may genuinely be gone. Re-boot rather than guess.
|
|
116
|
+
await reload();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const getToken = async () => {
|
|
120
|
+
// A 5s skew so a token that would die inside the caller's request is
|
|
121
|
+
// refreshed first rather than handed over.
|
|
122
|
+
const usable = cache.get(5_000);
|
|
123
|
+
if (usable)
|
|
124
|
+
return usable.jwt;
|
|
125
|
+
await refresh();
|
|
126
|
+
return cache.get(5_000)?.jwt ?? null;
|
|
127
|
+
};
|
|
128
|
+
// The FAPI client for the token-vault read. Reuses the same publishable key
|
|
129
|
+
// and fetch so `@atlasauth/js` stays the single implementation.
|
|
130
|
+
const fapiClient = new FapiClient({ publishableKey, baseUrl, fetchImpl: doFetch });
|
|
131
|
+
const getProviderToken = (provider) => fetchProviderToken(fapiClient, provider);
|
|
132
|
+
const signOut = async () => {
|
|
133
|
+
const current = cache.peek();
|
|
134
|
+
if (current) {
|
|
135
|
+
await call(`/v1/client/sessions/${current.sessionId}/revoke`, { method: 'POST' }).catch(() => undefined);
|
|
136
|
+
}
|
|
137
|
+
cache.clear();
|
|
138
|
+
setState({ status: 'signed_out', user: null, session: null, claims: null, memberships: [] });
|
|
139
|
+
scheduleRefresh();
|
|
140
|
+
};
|
|
141
|
+
const setActiveOrganization = async (organizationId) => {
|
|
142
|
+
const current = cache.peek();
|
|
143
|
+
if (!current)
|
|
144
|
+
return;
|
|
145
|
+
const response = await call(`/v1/client/sessions/${current.sessionId}/touch`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
body: JSON.stringify({ active_organization_id: organizationId }),
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok)
|
|
150
|
+
return;
|
|
151
|
+
const body = (await response.json());
|
|
152
|
+
// The new token carries the new org claims; re-boot would work too but
|
|
153
|
+
// costs a round trip for information already in hand.
|
|
154
|
+
applyToken(body.jwt, current.sessionId);
|
|
155
|
+
await reload();
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Complete a redirect-based sign-in. An OAuth or hosted-page flow lands back
|
|
159
|
+
* with a one-time ticket in the URL; exchange it for cookies, scrub the URL so
|
|
160
|
+
* a reload can't replay it, THEN boot. A redirect carrying only a status (a
|
|
161
|
+
* second factor still owed) has no ticket, so nothing is exchanged and the app
|
|
162
|
+
* boots signed-out — exactly as intended.
|
|
163
|
+
*/
|
|
164
|
+
const boot = async () => {
|
|
165
|
+
const result = !isServer && typeof window !== 'undefined'
|
|
166
|
+
? readRedirectResult(window.location.search)
|
|
167
|
+
: null;
|
|
168
|
+
if (result?.ticket) {
|
|
169
|
+
await call('/v1/client/tickets/exchange', {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
body: JSON.stringify({ attempt_id: result.attemptId, ticket: result.ticket }),
|
|
172
|
+
}).catch(() => undefined);
|
|
173
|
+
}
|
|
174
|
+
if (result && typeof window !== 'undefined') {
|
|
175
|
+
window.history.replaceState({}, document.title, stripRedirectParams(window.location.href));
|
|
176
|
+
}
|
|
177
|
+
await reload();
|
|
178
|
+
};
|
|
179
|
+
// `client` is referenced inside the mount seams; the arrows only run once the
|
|
180
|
+
// `const` is initialised, so the self-reference is sound.
|
|
181
|
+
const client = {
|
|
182
|
+
publishableKey,
|
|
183
|
+
frontendApi: baseUrl,
|
|
184
|
+
state,
|
|
185
|
+
pendingAttempt,
|
|
186
|
+
getToken,
|
|
187
|
+
getProviderToken,
|
|
188
|
+
signOut,
|
|
189
|
+
setActiveOrganization,
|
|
190
|
+
reload,
|
|
191
|
+
mountSignIn: (el) => mountSignInWidget(client, el),
|
|
192
|
+
mountSignUp: (el) => mountSignUpWidget(client, el),
|
|
193
|
+
mountUserButton: (el) => mountUserButtonWidget(client, el),
|
|
194
|
+
};
|
|
195
|
+
// Clear the refresh timer WITHOUT revoking the session — the provider calls
|
|
196
|
+
// this on unmount. `signOut()` would log the user out server-side, which is
|
|
197
|
+
// emphatically not what unmounting a React/Vue/Solid tree should do.
|
|
198
|
+
const dispose = () => {
|
|
199
|
+
if (timer) {
|
|
200
|
+
clearTimeout(timer);
|
|
201
|
+
timer = null;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
return { client, boot, dispose };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The provider component. Builds the client, provides it, and boots the session
|
|
208
|
+
* on mount — guarded to the browser: on the server there is no redirect to
|
|
209
|
+
* complete and no cookie jar to read, so the app renders `loading` and hydrates
|
|
210
|
+
* into the real status on the client. The refresh timer is cleared on unmount.
|
|
211
|
+
*/
|
|
212
|
+
export function AtlasProvider(props) {
|
|
213
|
+
const { client, boot, dispose } = createAtlasClient({
|
|
214
|
+
publishableKey: props.publishableKey,
|
|
215
|
+
frontendApi: props.frontendApi,
|
|
216
|
+
fetchImpl: props.fetchImpl,
|
|
217
|
+
});
|
|
218
|
+
if (!isServer) {
|
|
219
|
+
onMount(() => {
|
|
220
|
+
void boot();
|
|
221
|
+
});
|
|
222
|
+
// The client's refresh timer is a resource that outlives a render; drop it
|
|
223
|
+
// when the provider unmounts so a torn-down app does not keep polling.
|
|
224
|
+
onCleanup(dispose);
|
|
225
|
+
}
|
|
226
|
+
return h(AtlasContext.Provider, { value: client }, () => props.children);
|
|
227
|
+
}
|
|
228
|
+
function decodeClaims(jwt) {
|
|
229
|
+
const parts = jwt.split('.');
|
|
230
|
+
if (parts.length !== 3)
|
|
231
|
+
return null;
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public auth shapes the Solid SDK surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Kept identical to `@atlasauth/vue`'s `types.ts` on purpose: a customer moving
|
|
5
|
+
* a component between frameworks should read the same `user.first_name` and the
|
|
6
|
+
* same `claims.org_role`, not relearn the model. These mirror the FAPI
|
|
7
|
+
* `/v1/client` response, so the field names are the server's, not ours.
|
|
8
|
+
*/
|
|
9
|
+
export interface SessionClaims {
|
|
10
|
+
sub: string;
|
|
11
|
+
sid: string;
|
|
12
|
+
exp: number;
|
|
13
|
+
org_id?: string;
|
|
14
|
+
org_slug?: string;
|
|
15
|
+
org_role?: string;
|
|
16
|
+
org_permissions?: string[];
|
|
17
|
+
mfa?: boolean;
|
|
18
|
+
[claim: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
export interface AtlasUser {
|
|
21
|
+
id: string;
|
|
22
|
+
first_name: string | null;
|
|
23
|
+
last_name: string | null;
|
|
24
|
+
username: string | null;
|
|
25
|
+
image_url: string | null;
|
|
26
|
+
public_metadata: Record<string, unknown>;
|
|
27
|
+
unsafe_metadata: Record<string, unknown>;
|
|
28
|
+
mfa_enabled: boolean;
|
|
29
|
+
has_password: boolean;
|
|
30
|
+
email_addresses?: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
email_address: string;
|
|
33
|
+
verified: boolean;
|
|
34
|
+
primary: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export interface AtlasOrganizationMembership {
|
|
38
|
+
role: string;
|
|
39
|
+
organization: {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
slug: string;
|
|
43
|
+
image_url: string | null;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface AtlasSession {
|
|
47
|
+
id: string;
|
|
48
|
+
status: string;
|
|
49
|
+
last_active_organization_id: string | null;
|
|
50
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public auth shapes the Solid SDK surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Kept identical to `@atlasauth/vue`'s `types.ts` on purpose: a customer moving
|
|
5
|
+
* a component between frameworks should read the same `user.first_name` and the
|
|
6
|
+
* same `claims.org_role`, not relearn the model. These mirror the FAPI
|
|
7
|
+
* `/v1/client` response, so the field names are the server's, not ours.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type JSX } from 'solid-js';
|
|
2
|
+
import type { AtlasClient } from './provider';
|
|
3
|
+
export declare function mountSignInWidget(client: AtlasClient, el: HTMLElement): () => void;
|
|
4
|
+
export declare function mountSignUpWidget(client: AtlasClient, el: HTMLElement): () => void;
|
|
5
|
+
export declare function mountUserButtonWidget(client: AtlasClient, el: HTMLElement): () => void;
|
|
6
|
+
export declare const SignIn: () => JSX.Element;
|
|
7
|
+
export declare const SignUp: () => JSX.Element;
|
|
8
|
+
export declare const UserButton: () => JSX.Element;
|
package/dist/widgets.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Show, createEffect, createSignal, onCleanup, onMount } from 'solid-js';
|
|
2
|
+
import { isServer, render } from 'solid-js/web';
|
|
3
|
+
import { h } from './h';
|
|
4
|
+
import { FapiClient, advance, flowFromPending, nextStep, pollAttempt, shouldKeepPolling, } from '@atlasauth/js';
|
|
5
|
+
import { useAtlas } from './context';
|
|
6
|
+
/** A labelled, UNCONTROLLED input — its value is read from the form's
|
|
7
|
+
* `FormData` on submit, which sidesteps per-keystroke signal churn for a plain
|
|
8
|
+
* auth form. */
|
|
9
|
+
function field(name, label, type = 'text') {
|
|
10
|
+
return h('label', { class: 'atlas-field' }, [
|
|
11
|
+
h('span', label),
|
|
12
|
+
h('input', { class: 'atlas-input', name, type }),
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
15
|
+
/** Form-level (non-field) errors, rendered as an alert. */
|
|
16
|
+
function renderErrors(errors) {
|
|
17
|
+
return errors
|
|
18
|
+
.filter((error) => !error.param)
|
|
19
|
+
.map((error) => h('p', { class: 'atlas-error', role: 'alert' }, error.message));
|
|
20
|
+
}
|
|
21
|
+
/** The fields for the current server-driven step. The `unknown`/default branch
|
|
22
|
+
* says so out loud rather than rendering a blank box the user cannot act on. */
|
|
23
|
+
function renderFields(kind) {
|
|
24
|
+
switch (kind) {
|
|
25
|
+
case 'collect_identifier':
|
|
26
|
+
return [field('identifier', 'Email', 'email')];
|
|
27
|
+
case 'collect_first_factor':
|
|
28
|
+
return [field('password', 'Password', 'password')];
|
|
29
|
+
case 'collect_second_factor':
|
|
30
|
+
return [field('code', 'Two-factor code')];
|
|
31
|
+
case 'enroll_second_factor':
|
|
32
|
+
// Two codes, not one: consecutive codes catch a phone with a fast clock
|
|
33
|
+
// here rather than on every sign-in afterwards.
|
|
34
|
+
return [
|
|
35
|
+
field('code', 'Authenticator code'),
|
|
36
|
+
field('secondCode', 'Next authenticator code'),
|
|
37
|
+
];
|
|
38
|
+
case 'collect_email_code':
|
|
39
|
+
return [field('code', 'Email code')];
|
|
40
|
+
case 'collect_new_password':
|
|
41
|
+
return [field('password', 'New password', 'password')];
|
|
42
|
+
case 'await_oauth':
|
|
43
|
+
return [h('p', 'Continue in the window that opened.')];
|
|
44
|
+
case 'done':
|
|
45
|
+
return [h('p', 'Signed in — you can close this.')];
|
|
46
|
+
default:
|
|
47
|
+
return [h('p', { class: 'atlas-error' }, 'This step needs an updated app. Please reload.')];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function renderSubmit(kind, busy) {
|
|
51
|
+
if (kind === 'done' || kind === 'await_oauth')
|
|
52
|
+
return null;
|
|
53
|
+
return h('button', { class: 'atlas-button', type: 'submit', disabled: busy }, 'Continue');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The server-driven sign-in / sign-up flow. Never decides what comes next: it
|
|
57
|
+
* drives through `advance` (the driver owns the step→endpoint mapping) and
|
|
58
|
+
* renders whatever `status` the server returned. `<SignIn>` and `<SignUp>` are
|
|
59
|
+
* the same driver — only the title differs, exactly as in React and Vue.
|
|
60
|
+
*/
|
|
61
|
+
function Flow(props) {
|
|
62
|
+
const client = props.client;
|
|
63
|
+
const fapi = new FapiClient({ publishableKey: client.publishableKey, baseUrl: client.frontendApi });
|
|
64
|
+
// Seeded from a redirect that returned mid-flow (OAuth owing a second
|
|
65
|
+
// factor), so the user resumes at the demanded step instead of restarting.
|
|
66
|
+
const [state, setState] = createSignal(flowFromPending(client.pendingAttempt));
|
|
67
|
+
const submit = (values) => {
|
|
68
|
+
void (async () => {
|
|
69
|
+
setState((s) => ({ ...s, busy: true, errors: [] }));
|
|
70
|
+
const next = await advance(fapi, state(), values);
|
|
71
|
+
setState(next);
|
|
72
|
+
// A completion response carries a one-time ticket (§7.1). FAPI is
|
|
73
|
+
// cross-origin to the app and cannot set the cookie on its own response,
|
|
74
|
+
// so exchange the ticket over a request THEN re-boot, so the rest of the
|
|
75
|
+
// app sees a signed-in user — and no session token ever rode in a URL.
|
|
76
|
+
if (next.attempt?.status === 'complete') {
|
|
77
|
+
if (next.ticket) {
|
|
78
|
+
await fapi.post('/v1/client/tickets/exchange', {
|
|
79
|
+
attempt_id: next.attempt.id,
|
|
80
|
+
ticket: next.ticket,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
await client.reload();
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
};
|
|
87
|
+
// §5.3 magic-link poll: a sign-in finished on another device. Restart on each
|
|
88
|
+
// state change, run only while the state says to, stop on teardown.
|
|
89
|
+
let poller = null;
|
|
90
|
+
const stopPolling = () => {
|
|
91
|
+
if (poller) {
|
|
92
|
+
clearInterval(poller);
|
|
93
|
+
poller = null;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
createEffect(() => {
|
|
97
|
+
const current = state();
|
|
98
|
+
stopPolling();
|
|
99
|
+
if (!shouldKeepPolling(current))
|
|
100
|
+
return;
|
|
101
|
+
poller = setInterval(() => {
|
|
102
|
+
void (async () => {
|
|
103
|
+
const { state: next, ticket } = await pollAttempt(fapi, state());
|
|
104
|
+
setState(next);
|
|
105
|
+
if (!ticket || !next.attempt)
|
|
106
|
+
return;
|
|
107
|
+
await fapi.post('/v1/client/tickets/exchange', {
|
|
108
|
+
attempt_id: next.attempt.id,
|
|
109
|
+
ticket,
|
|
110
|
+
});
|
|
111
|
+
await client.reload();
|
|
112
|
+
})();
|
|
113
|
+
}, 2_000);
|
|
114
|
+
});
|
|
115
|
+
onCleanup(stopPolling);
|
|
116
|
+
const step = () => {
|
|
117
|
+
const attempt = state().attempt;
|
|
118
|
+
return attempt ? nextStep(attempt) : { kind: 'collect_identifier' };
|
|
119
|
+
};
|
|
120
|
+
return h('form', {
|
|
121
|
+
class: 'atlas-card',
|
|
122
|
+
onSubmit: (event) => {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
const form = event.currentTarget;
|
|
125
|
+
const values = {};
|
|
126
|
+
new FormData(form).forEach((value, key) => {
|
|
127
|
+
values[key] = String(value);
|
|
128
|
+
});
|
|
129
|
+
submit(values);
|
|
130
|
+
},
|
|
131
|
+
}, [
|
|
132
|
+
h('h1', { class: 'atlas-title' }, props.title),
|
|
133
|
+
() => renderErrors(state().errors),
|
|
134
|
+
() => renderFields(step().kind),
|
|
135
|
+
() => renderSubmit(step().kind, state().busy),
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
/** The signed-in user's avatar + a menu whose only built-in action is sign-out
|
|
139
|
+
* — the Solid peer of Vue's `<UserButton>`. Renders nothing when signed out. */
|
|
140
|
+
function UserButtonView(client) {
|
|
141
|
+
const [open, setOpen] = createSignal(false);
|
|
142
|
+
const user = () => client.state.user;
|
|
143
|
+
const label = () => {
|
|
144
|
+
const u = user();
|
|
145
|
+
if (!u)
|
|
146
|
+
return '';
|
|
147
|
+
return [u.first_name, u.last_name].filter(Boolean).join(' ') || u.username || '';
|
|
148
|
+
};
|
|
149
|
+
return h(Show, { when: () => Boolean(user()) }, () => h('div', { class: 'atlas-user-button' }, [
|
|
150
|
+
h('button', {
|
|
151
|
+
class: 'atlas-avatar',
|
|
152
|
+
'aria-expanded': () => open(),
|
|
153
|
+
onClick: () => setOpen(!open()),
|
|
154
|
+
}, () => {
|
|
155
|
+
const u = user();
|
|
156
|
+
return u?.image_url
|
|
157
|
+
? h('img', { alt: '', src: u.image_url })
|
|
158
|
+
: h('span', () => label().slice(0, 1));
|
|
159
|
+
}),
|
|
160
|
+
() => open()
|
|
161
|
+
? h('div', { class: 'atlas-menu', role: 'menu' }, [
|
|
162
|
+
h('span', { class: 'atlas-menu-item' }, () => label()),
|
|
163
|
+
h('button', { class: 'atlas-menu-item', onClick: () => void client.signOut() }, 'Sign out'),
|
|
164
|
+
])
|
|
165
|
+
: null,
|
|
166
|
+
]));
|
|
167
|
+
}
|
|
168
|
+
// --- Mount seams, called by the client's `mountX` methods. `render` returns the
|
|
169
|
+
// disposer that `onCleanup` later invokes to tear the nested root down. ---
|
|
170
|
+
export function mountSignInWidget(client, el) {
|
|
171
|
+
return render(() => Flow({ client, title: 'Sign in' }), el);
|
|
172
|
+
}
|
|
173
|
+
export function mountSignUpWidget(client, el) {
|
|
174
|
+
return render(() => Flow({ client, title: 'Sign up' }), el);
|
|
175
|
+
}
|
|
176
|
+
export function mountUserButtonWidget(client, el) {
|
|
177
|
+
return render(() => UserButtonView(client), el);
|
|
178
|
+
}
|
|
179
|
+
// --- The public, thin mountable components. ---
|
|
180
|
+
/**
|
|
181
|
+
* Build a component that mounts one widget. It calls the CLIENT's seam
|
|
182
|
+
* (`client.mountSignIn` etc.) rather than the module function directly, so the
|
|
183
|
+
* mounted root reaches the same client the provider built — and so the seam is a
|
|
184
|
+
* real, test-observable boundary. The host element is created imperatively
|
|
185
|
+
* (not via a reactive `ref`) so it is guaranteed present the moment `onMount`
|
|
186
|
+
* fires. SSR renders an empty host and hydrates on the client.
|
|
187
|
+
*/
|
|
188
|
+
function mountable(seam, rootClass) {
|
|
189
|
+
return () => {
|
|
190
|
+
const client = useAtlas();
|
|
191
|
+
if (isServer)
|
|
192
|
+
return h('div', { class: rootClass });
|
|
193
|
+
const container = document.createElement('div');
|
|
194
|
+
container.className = rootClass;
|
|
195
|
+
onMount(() => {
|
|
196
|
+
// The seam drives a nested Solid root into `container`; its disposer is
|
|
197
|
+
// the whole teardown, so unmounting the component unmounts the widget.
|
|
198
|
+
const dispose = seam(client)(container);
|
|
199
|
+
onCleanup(dispose);
|
|
200
|
+
});
|
|
201
|
+
return container;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export const SignIn = mountable((client) => client.mountSignIn, 'atlas-signin-root');
|
|
205
|
+
export const SignUp = mountable((client) => client.mountSignUp, 'atlas-signup-root');
|
|
206
|
+
export const UserButton = mountable((client) => client.mountUserButton, 'atlas-user-button-root');
|
|
207
|
+
//# sourceMappingURL=widgets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/solid",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@atlasauth/js": "0.4.0"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"solid-js": ">=1.8"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@solidjs/testing-library": "^0.8.10",
|
|
21
|
+
"jsdom": "^25.0.1",
|
|
22
|
+
"solid-js": "^1.8.0",
|
|
23
|
+
"typescript": "^5.6.3",
|
|
24
|
+
"vitest": "^2.1.4"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/alphajew420/ssoly.git",
|
|
36
|
+
"directory": "packages/solid"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/alphajew420/ssoly/tree/main/packages/solid#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/alphajew420/ssoly/issues"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"test": "vitest run"
|
|
46
|
+
}
|
|
6
47
|
}
|