@user-hub/auth 0.1.2 → 0.1.3

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 CHANGED
@@ -109,6 +109,17 @@ Run your dev server as usual and open it. With no token yet, it sends you to the
109
109
  - **Different port:** run `hub-user register` again with the new `--dev` URL. The hub only sends tokens to registered URLs.
110
110
  - **Production:** someone without a token sees "Scan your badge to sign in". Pass `signedOut={…}` to `<HubSignedIn>` to style that screen.
111
111
 
112
+ ## Sign out
113
+
114
+ ```tsx
115
+ import { useHubSignOut } from "@user-hub/auth/react"; // also from /convex-react
116
+
117
+ const signOut = useHubSignOut();
118
+ <button onClick={signOut}>Sign out</button>
119
+ ```
120
+
121
+ It forgets this app's token; other apps stay signed in. In production the person sees "Scan your badge to sign in". On a local dev server the hub opens so you can pick who to be next.
122
+
112
123
  ## Facilitator pages
113
124
 
114
125
  People are members or facilitators. Always check on the server, then hide what members cannot use:
@@ -158,7 +169,7 @@ Apps without Convex use `HubAuthProvider` and `HubSignedIn` from `@user-hub/auth
158
169
  | `@user-hub/auth/convex-react` | `ConvexHubAuthProvider` | For Convex apps, in place of `ConvexProvider`, inside `<body>`. Props: `client`, `app`, `hubUrl?` |
159
170
  | `@user-hub/auth/react` | `HubAuthProvider` | The same without Convex: `app`, `hubUrl?` |
160
171
  | both | `HubSignedIn` | Shows its children only when signed in. Props: `signedOut?`, `loading?` |
161
- | | `useHubUser()`, `useHubToken()` | The person (or null), the raw token (or null). Also from `/convex-react` |
172
+ | | `useHubUser()`, `useHubToken()`, `useHubSignOut()` | The person (or null), the raw token (or null), a sign-out function. Also from `/convex-react` |
162
173
  | `@user-hub/auth/server` | `verifyHubToken(requestOrToken, { app })` | Checks a token anywhere else |
163
174
  | CLI | `hub-user register --name <name> [--prod <url>] [--dev <url>] [--hub <url>] [--json]` | Registers or updates the app (at least one URL) |
164
175
 
@@ -171,4 +182,4 @@ bun run check # Biome
171
182
  bun run build # dist/: commit it
172
183
  ```
173
184
 
174
- Publish with `bun publish`: it builds and runs the tests first.
185
+ Release with `just release patch` (or `minor` / `major`): it bumps the version, publishes to npm and pushes the commit and tag. See the comments in the `justfile`.
@@ -1,6 +1,6 @@
1
1
  import { type ConvexReactClient } from "convex/react";
2
2
  import { type HubAuthProviderProps } from "./react";
3
- export { HubSignedIn, useHubSession, useHubToken, useHubUser, } from "./react";
3
+ export { HubSignedIn, useHubSession, useHubSignOut, useHubToken, useHubUser, } from "./react";
4
4
  export type { HubRole, HubUser } from "./token";
5
5
  /**
6
6
  * HubAuthProvider for apps on Convex: the hub token becomes the Convex
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  HubAuthProvider2,
3
3
  HubSignedIn2,
4
+ useHubSignOut2,
4
5
  useHubSession2,
5
6
  useHubUser2,
6
7
  useHubToken2
7
- } from "./convex-7n6dqf5r.js";
8
+ } from "./convex-zgnvvwvn.js";
8
9
 
9
10
  // src/convex-react.tsx
10
11
  import { ConvexProviderWithAuth } from "convex/react";
@@ -36,6 +37,7 @@ export {
36
37
  ConvexHubAuthProvider,
37
38
  HubSignedIn2 as HubSignedIn,
38
39
  useHubSession2 as useHubSession,
40
+ useHubSignOut2 as useHubSignOut,
39
41
  useHubToken2 as useHubToken,
40
42
  useHubUser2 as useHubUser
41
43
  };
@@ -1,6 +1,7 @@
1
1
  // src/react.tsx
2
2
  import {
3
3
  createContext,
4
+ useCallback,
4
5
  useContext,
5
6
  useEffect,
6
7
  useMemo,
@@ -41,7 +42,8 @@ var MAX_TIMEOUT_MS = 2 ** 31 - 1;
41
42
  var LOCAL_HOST = /^(localhost|127(\.\d{1,3}){3}|10(\.\d{1,3}){3}|192\.168(\.\d{1,3}){2}|172\.(1[6-9]|2\d|3[01])(\.\d{1,3}){2}|\[::1\]|[a-z0-9-]+\.local)$/i;
42
43
  var HubContext = createContext({
43
44
  session: undefined,
44
- redirecting: false
45
+ redirecting: false,
46
+ signOut: () => {}
45
47
  });
46
48
  var storageKey = (app) => `user-hub-token:${app}`;
47
49
  function readStorage(key) {
@@ -95,7 +97,11 @@ function HubAuthProvider2({
95
97
  window.location.replace(`${hubUrl.replace(/\/+$/, "")}/open/${encodeURIComponent(app)}`);
96
98
  }
97
99
  }, [redirecting, hubUrl, app]);
98
- const state = useMemo(() => ({ session, redirecting }), [session, redirecting]);
100
+ const signOut = useCallback(() => {
101
+ writeStorage(storageKey(app), null);
102
+ setSession(null);
103
+ }, [app]);
104
+ const state = useMemo(() => ({ session, redirecting, signOut }), [session, redirecting, signOut]);
99
105
  return /* @__PURE__ */ jsx(HubContext.Provider, {
100
106
  value: state,
101
107
  children
@@ -117,6 +123,9 @@ function HubSignedIn2({
117
123
  return loading;
118
124
  return session ? children : signedOut;
119
125
  }
126
+ function useHubSignOut2() {
127
+ return useContext(HubContext).signOut;
128
+ }
120
129
  function useHubSession2() {
121
130
  return useContext(HubContext).session;
122
131
  }
@@ -128,4 +137,4 @@ function useHubToken2() {
128
137
  return useHubSession2()?.token ?? null;
129
138
  }
130
139
 
131
- export { HubAuthProvider2, HubSignedIn2, useHubSession2, useHubUser2, useHubToken2 };
140
+ export { HubAuthProvider2, HubSignedIn2, useHubSignOut2, useHubSession2, useHubUser2, useHubToken2 };
package/dist/react.d.ts CHANGED
@@ -22,6 +22,12 @@ export declare function HubSignedIn({ children, signedOut, loading, }: {
22
22
  signedOut?: ReactNode;
23
23
  loading?: ReactNode;
24
24
  }): ReactNode;
25
+ /**
26
+ * Signs the person out of this app: `const signOut = useHubSignOut()`. They
27
+ * need to scan their badge again (or, on a dev server, pick a person at the
28
+ * hub). Other apps stay signed in.
29
+ */
30
+ export declare function useHubSignOut(): () => void;
25
31
  /** The current session: undefined while loading, null when signed out. */
26
32
  export declare function useHubSession(): HubSession | null | undefined;
27
33
  /** The signed-in person, or null. */
package/dist/react.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  HubAuthProvider2,
3
3
  HubSignedIn2,
4
+ useHubSignOut2,
4
5
  useHubSession2,
5
6
  useHubUser2,
6
7
  useHubToken2
7
- } from "./convex-7n6dqf5r.js";
8
+ } from "./convex-zgnvvwvn.js";
8
9
  export {
9
10
  HubAuthProvider2 as HubAuthProvider,
10
11
  HubSignedIn2 as HubSignedIn,
11
12
  useHubSession2 as useHubSession,
13
+ useHubSignOut2 as useHubSignOut,
12
14
  useHubToken2 as useHubToken,
13
15
  useHubUser2 as useHubUser
14
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@user-hub/auth",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Sign people in from User Hub badges: Convex auth config, React provider, server verification and the hub-user CLI.",
5
5
  "type": "module",
6
6
  "files": [
@@ -51,6 +51,7 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@biomejs/biome": "2.5.15",
54
+ "@happy-dom/global-registrator": "^20.14.5",
54
55
  "@types/bun": "^1.4.2",
55
56
  "@types/react": "^19.3.0",
56
57
  "@types/react-dom": "^19.3.0",
@@ -7,6 +7,26 @@ description: Sign people into an app from their User Hub badge. Use when an app
7
7
 
8
8
  People at a workshop scan a printed QR badge. The hub sends them to whichever app is live, signed in, with a token in the URL fragment (`#token=…`) that only that app accepts. This package turns that token into a signed-in person. The app keeps its own data, keyed on the person's hub id.
9
9
 
10
+ ## Starting a new app
11
+
12
+ If there is no app yet, don't build one by hand: scaffold one with sign-in already wired (TanStack Start + Convex, sign-in and sign-out, a facilitator page, `CLAUDE.md` and this skill):
13
+
14
+ ```sh
15
+ bun create @user-hub my-app
16
+ ```
17
+
18
+ In a terminal it asks for the hub's URL and admin password and saves them to the new app's `.env.local`. As an agent you never handle the password, so either ask the human to run it, or run it only when `HUB_URL` and `HUB_ADMIN_PASSWORD` are already in the environment (for example `infisical run -- bun create @user-hub my-app`). Never pass `--admin-password` with a value you typed yourself.
19
+
20
+ Then:
21
+
22
+ ```sh
23
+ cd my-app
24
+ bun run setup # Convex project, hub registration, HUB_ISSUER and HUB_JWKS, once
25
+ bun run dev # http://localhost:3000
26
+ ```
27
+
28
+ That covers every step below; read on only to add User Hub to an existing app or to change the wiring.
29
+
10
30
  ## The hub's URL
11
31
 
12
32
  The hub has one address, for example `https://hub.example.com`. The human gives it to you; you need it in three places, all with the same value:
@@ -94,6 +114,17 @@ Run the app as usual (`bun run dev`) on the `--dev` URL you registered. Opening
94
114
  - If the dev server runs on another port, run `hub-user register` again with the same `--name` and the new `--dev` URL: the hub only sends tokens back to registered URLs.
95
115
  - This only happens on local addresses (localhost, 127.x, 10.x, 172.16-31.x, 192.168.x, *.local). In production a person without a token sees "Scan your badge to sign in".
96
116
 
117
+ ## Sign out
118
+
119
+ Use `useHubSignOut()` from the same entry point as the provider; never clear storage or tokens yourself.
120
+
121
+ ```tsx
122
+ const signOut = useHubSignOut();
123
+ <button onClick={signOut}>Sign out</button>
124
+ ```
125
+
126
+ It forgets this app's token only. In production the person then sees the signed-out screen and scans their badge again; on a local dev server the hub opens to pick another person.
127
+
97
128
  ## Facilitator pages
98
129
 
99
130
  People are either `"member"` or `"facilitator"`, set in the hub. To give facilitators their own page or actions: