@adonis-agora/authkit-react 0.20.0 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-react",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "Frontend ergonomics over AuthKit for AdonisJS + Inertia + React apps: a typed useAuth() hook, role-gating hooks and gating components.",
5
5
  "license": "MIT",
6
6
  "author": "dudousxd",
@@ -20,7 +20,8 @@
20
20
  "authentication",
21
21
  "authkit",
22
22
  "rbac",
23
- "authorization"
23
+ "authorization",
24
+ "tanstack-intent"
24
25
  ],
25
26
  "publishConfig": {
26
27
  "access": "public"
@@ -30,7 +31,8 @@
30
31
  "types": "./build/index.d.ts",
31
32
  "files": [
32
33
  "build",
33
- "styles.css"
34
+ "styles.css",
35
+ "skills"
34
36
  ],
35
37
  "exports": {
36
38
  ".": "./build/index.js",
@@ -58,6 +60,7 @@
58
60
  "@japa/assert": "4.2.0",
59
61
  "@japa/runner": "5.3.0",
60
62
  "@poppinss/ts-exec": "1.4.4",
63
+ "@tanstack/intent": "^0.3.2",
61
64
  "@tanstack/react-query": "5.101.4",
62
65
  "@types/node": "25.9.1",
63
66
  "@types/react": "19.2.18",
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: authkit-react-auth
3
+ description: >-
4
+ Frontend auth ergonomics with @adonis-agora/authkit-react in AdonisJS + Inertia +
5
+ React apps. Covers the shared-prop `authkit` contract (AuthSharedProps:
6
+ { user, globalRoles }), useAuth() semantics (never throws; AuthProvider context
7
+ precedence), role helpers hasGlobalRole/hasAnyGlobalRole/hasAllGlobalRoles,
8
+ Authenticated/Guest gating components with fallback, AuthkitProvider config
9
+ ({ csrfToken, endpoints, idp }) with headless hooks (useSignIn, useSignOut,
10
+ useProfile, useSessions, useAuthorizedApps) and ready components (SignInButton,
11
+ UserButton, UserProfile, AuthorizedApps), and the idp:'external' degradation rule.
12
+ Use when wiring the auth shared prop, gating UI by roles, adding sign-in/out
13
+ buttons or account components, or debugging an always-unauthenticated React app.
14
+ license: MIT
15
+ metadata:
16
+ type: core
17
+ library: "@adonis-agora/authkit-react"
18
+ library_version: "0.20.0"
19
+ framework: react
20
+ sources:
21
+ - "DavideCarvalho/adonis-authkit:packages/authkit-react/README.md"
22
+ - "DavideCarvalho/adonis-authkit:packages/authkit-react/src/use_auth.ts"
23
+ - "DavideCarvalho/adonis-authkit:packages/authkit-react/src/config.ts"
24
+ - "DavideCarvalho/adonis-authkit:packages/authkit-react/index.ts"
25
+ ---
26
+
27
+ # React auth ergonomics (Inertia)
28
+
29
+ `@adonis-agora/authkit-react` never authenticates anything. It consumes auth state
30
+ the AdonisJS host already resolved (via `@adonis-agora/authkit-client`) and shares
31
+ with Inertia as a page prop — then gives you typed hooks, role helpers, gating
32
+ components and ready-made account UI on top.
33
+
34
+ ## Setup
35
+
36
+ Share the `authkit` prop from the host, typically in `config/inertia.ts`:
37
+
38
+ ```ts
39
+ // config/inertia.ts
40
+ import { defineConfig } from '@adonisjs/inertia'
41
+
42
+ export default defineConfig({
43
+ sharedData: {
44
+ authkit: async (ctx) => {
45
+ const auth = ctx.authkit // your Authenticator instance
46
+ const user = await auth.getUser() // same shape as identityToUser/resolveUser
47
+ return {
48
+ user: user ?? null,
49
+ globalRoles: auth.identity?.globalRoles ?? [],
50
+ }
51
+ },
52
+ },
53
+ })
54
+ ```
55
+
56
+ Then consume it in any component:
57
+
58
+ ```tsx
59
+ import { useAuth } from '@adonis-agora/authkit-react'
60
+
61
+ function Header() {
62
+ const { user, isAuthenticated, hasGlobalRole } = useAuth()
63
+
64
+ if (!isAuthenticated) return <a href="/login">Entrar</a>
65
+
66
+ return (
67
+ <div>
68
+ Olá, {user!.name ?? user!.email}
69
+ {hasGlobalRole('ADMIN') && <a href="/admin">Admin</a>}
70
+ </div>
71
+ )
72
+ }
73
+ ```
74
+
75
+ Source: `packages/authkit-react/README.md` §§ 1–2.
76
+
77
+ ## Core patterns
78
+
79
+ ### Pattern 1 — role gating with `Authenticated` / `Guest`
80
+
81
+ ```tsx
82
+ import { Authenticated, Guest } from '@adonis-agora/authkit-react'
83
+
84
+ <Authenticated fallback={<LoginButton />}>
85
+ <Dashboard />
86
+ </Authenticated>
87
+
88
+ <Guest>
89
+ <MarketingBanner />
90
+ </Guest>
91
+ ```
92
+
93
+ For pure functions outside components:
94
+
95
+ ```ts
96
+ import { hasGlobalRole, hasAnyGlobalRole, hasAllGlobalRoles } from '@adonis-agora/authkit-react'
97
+
98
+ hasGlobalRole(user, 'ADMIN')
99
+ hasAnyGlobalRole(user, ['ADMIN', 'TEACHER'])
100
+ ```
101
+
102
+ Source: `packages/authkit-react/README.md` §§ 3 and "Helpers puros".
103
+
104
+ ### Pattern 2 — `AuthProvider` outside Inertia
105
+
106
+ In tests or Storybook there are no page props; inject state manually. Context takes
107
+ precedence over Inertia props when present:
108
+
109
+ ```tsx
110
+ import { AuthProvider } from '@adonis-agora/authkit-react'
111
+
112
+ <AuthProvider value={{ user, globalRoles: user.globalRoles }}>
113
+ <App />
114
+ </AuthProvider>
115
+ ```
116
+
117
+ Source: `packages/authkit-react/README.md` § 4.
118
+
119
+ ### Pattern 3 — `AuthkitProvider` + ready components
120
+
121
+ Wrap the app once to configure URLs/CSRF, then use headless hooks and prebuilt
122
+ components that talk to the host-kit's JSON endpoints:
123
+
124
+ ```tsx
125
+ import '@adonis-agora/authkit-react/styles.css'
126
+ import {
127
+ AuthkitProvider,
128
+ useSignIn, useSignOut,
129
+ UserButton, UserProfile, AuthorizedApps,
130
+ } from '@adonis-agora/authkit-react'
131
+
132
+ <AuthkitProvider config={{ csrfToken: page.props.csrfToken }}>
133
+ <UserButton />
134
+ <UserProfile />
135
+ <AuthorizedApps />
136
+ </AuthkitProvider>
137
+ ```
138
+
139
+ Endpoint defaults point at real host-kit routes (`/auth/login`, `/account/logout`,
140
+ `/account/security/profile`, `/account/apps`, …). In a *client-app* topology point
141
+ them at local routes that redirect/proxy to the IdP; theming via CSS vars
142
+ `--authkit-*`.
143
+
144
+ Source: `packages/authkit-react/README.md` § 5, `src/config.ts` (`DEFAULT_CONFIG`,
145
+ `AuthkitConfig`).
146
+
147
+ ### Pattern 4 — third-party IdP mode
148
+
149
+ When the backend authenticates against another IdP (via
150
+ `@adonis-agora/authkit-client`), declare it so data-dependent components degrade
151
+ instead of calling endpoints that do not exist:
152
+
153
+ ```tsx
154
+ <AuthkitProvider config={{ idp: 'external' }}>
155
+ {/* works: SignInButton, SignOutButton, useAuth, Avatar */}
156
+ {/* degrades to null: UserProfile, OrganizationSwitcher, AuthorizedApps */}
157
+ </AuthkitProvider>
158
+ ```
159
+
160
+ Source: `src/config.ts` (`AuthkitIdpMode` JSDoc).
161
+
162
+ ## Common mistakes
163
+
164
+ ### HIGH — Never sharing the `authkit` prop from the host
165
+
166
+ ```ts
167
+ // Wrong — no sharedData; every page renders unauthenticated
168
+ export default defineConfig({ sharedData: {} })
169
+ ```
170
+
171
+ ```ts
172
+ // Correct — share { user, globalRoles } under the `authkit` key
173
+ sharedData: {
174
+ authkit: async (ctx) => {
175
+ const auth = ctx.authkit
176
+ return {
177
+ user: (await auth.getUser()) ?? null,
178
+ globalRoles: auth.identity?.globalRoles ?? [],
179
+ }
180
+ },
181
+ }
182
+ ```
183
+
184
+ `useAuth()` deliberately never throws on a missing prop — it falls back to
185
+ unauthenticated state (`user: null`, empty lists, dev-only console.warn), so a host
186
+ that forgot the prop ships a UI where nobody ever appears logged in.
187
+
188
+ Source: `src/use_auth.ts` (fallback + warn), README § 1.
189
+
190
+ ### MEDIUM — Feeding a user object that does not match `AuthUser`
191
+
192
+ ```ts
193
+ // Wrong — raw token claims passed as the user
194
+ sharedData: { authkit: async (ctx) => ({ user: await ctx.authkit.getIdentity() }) }
195
+ ```
196
+
197
+ ```ts
198
+ // Correct — resolveUser/identityToUser output satisfies AuthUser
199
+ const user = await ctx.authkit.getUser()
200
+ return { user: user ?? null, globalRoles: ctx.authkit.identity?.globalRoles ?? [] }
201
+ ```
202
+
203
+ The prop's `user` must match `{ id, email, name?, avatarUrl?, globalRoles }` —
204
+ exactly what `identityToUser`/`resolveUser` produce. Raw identity claims lack that
205
+ shape and break typed consumers downstream.
206
+
207
+ Source: `packages/authkit-react/README.md` § 1 ("O objeto `user` deve corresponder ao
208
+ tipo `AuthUser`").
209
+
210
+ ### MEDIUM — Expecting `<Can>` / permission gating here
211
+
212
+ ```tsx
213
+ // Wrong — looking for app-permission gating in authkit-react
214
+ import { Can } from '@adonis-agora/authkit-react' // not exported
215
+ ```
216
+
217
+ ```tsx
218
+ // Correct — global-role gates come from authkit; permissions live in authz-react
219
+ import { Authenticated } from '@adonis-agora/authkit-react'
220
+ import { CanPermission } from '@adonis-agora/authz-react' // consults the Authz service
221
+ ```
222
+
223
+ App-role/permission gating migrated to `@adonis-agora/authz-react`; this package
224
+ gates only on authentication and token-level `globalRoles`. (`useCan`/
225
+ `CanPermission` exist here only as thin callers of the `/authz/can` endpoint.)
226
+
227
+ Source: `packages/authkit-react/README.md` § 3 callout, `index.ts`
228
+ (`useCan`, `CanPermission`, `canPath` endpoint).
229
+
230
+ See also: `authkit-rp-client/SKILL.md` — the host-side resolver choices decide what
231
+ reaches the `authkit` prop.