@ciromaciel/auth-react 1.0.0 → 1.0.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 +102 -92
- package/package.json +8 -9
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ciromaciel/auth-react
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://www.npmjs.com/package/@ciromaciel/auth-react" target="_blank"><img src="https://img.shields.io/npm/v/@ciromaciel/auth-react.svg" alt="npm version" /></a>
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
<a href="https://www.npmjs.com/package/@ciromaciel/auth-react" target="_blank"><img src="https://img.shields.io/npm/l/@ciromaciel/auth-react.svg" alt="license" /></a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Passwordless authentication for React: a code arrives by email, and it becomes a JWT session.
|
|
10
|
+
|
|
11
|
+
There is one way in. No password, no sign-up screen, no reset flow — the account is created and
|
|
12
|
+
verified the first time a code is presented. Social sign-in is available when the application
|
|
13
|
+
owner enables a provider.
|
|
10
14
|
|
|
11
15
|
## Installation
|
|
12
16
|
|
|
@@ -14,32 +18,28 @@ Auth SDK for React with JWT and JWKS.
|
|
|
14
18
|
bun add @ciromaciel/auth-react
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
Peer dependencies: `react`, `react-dom`, `react-router-dom` and `zustand`. The pre-built screens
|
|
22
|
+
also need `@mantine/core`, `@mantine/form`, `@mantine/hooks` and `@tabler/icons-react` — all
|
|
23
|
+
optional if you build your own UI from the hooks.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
18
26
|
|
|
19
27
|
```jsx
|
|
20
|
-
import { AuthProvider,
|
|
28
|
+
import { AuthProvider, Protect, SignIn, SignedIn, SignedOut, SignInButton } from '@ciromaciel/auth-react'
|
|
21
29
|
|
|
22
|
-
// 1. Wrap your app with AuthProvider
|
|
23
30
|
function App() {
|
|
24
31
|
return (
|
|
25
32
|
<AuthProvider apiKey="your-api-key">
|
|
26
33
|
<Routes>
|
|
27
|
-
<Route
|
|
28
|
-
|
|
29
|
-
element={<
|
|
30
|
-
/>
|
|
31
|
-
<Route element={<Protect />}>
|
|
32
|
-
<Route
|
|
33
|
-
path="/"
|
|
34
|
-
element={<Home />}
|
|
35
|
-
/>
|
|
34
|
+
<Route path="/login" element={<SignIn />} />
|
|
35
|
+
<Route element={<Protect redirectTo="/login" />}>
|
|
36
|
+
<Route path="/" element={<Home />} />
|
|
36
37
|
</Route>
|
|
37
38
|
</Routes>
|
|
38
39
|
</AuthProvider>
|
|
39
40
|
)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
// 2. Use control components for conditional rendering
|
|
43
43
|
function Header() {
|
|
44
44
|
return (
|
|
45
45
|
<header>
|
|
@@ -54,116 +54,126 @@ function Header() {
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### Authentication Components
|
|
60
|
-
|
|
61
|
-
| Component | Description |
|
|
62
|
-
| --------------------- | --------------------------------------------------------------- |
|
|
63
|
-
| `<SignIn />` | The whole way in: asks for the email, then takes the emailed code |
|
|
64
|
-
| `<UserProfile />` | User profile management modal |
|
|
65
|
-
| `<UserInformation />` | Flexible user details and account menu |
|
|
66
|
-
| `<SocialButtons />` | Sign-in buttons for the providers the application enabled |
|
|
57
|
+
`AuthProvider` takes `apiKey` (required), `apiUrl`, `internal` and `onError`. In `internal` mode
|
|
58
|
+
the API key is not required — that is for same-origin applications.
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
were four answers to the same question — does this person control this inbox? —
|
|
70
|
-
and three of them answered it with a URL. The code answers it with none, and the
|
|
71
|
-
account is created, and verified, the first time one is presented.
|
|
60
|
+
## Signing in
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
person goes to the provider's consent screen and comes back. `<SignIn />`
|
|
75
|
-
renders those buttons on its own — the `socialLogin` prop defaults to `'auto'`,
|
|
76
|
-
which shows whatever the application owner enabled in the Auth panel and nothing
|
|
77
|
-
at all when they enabled none. An application with no provider configured
|
|
78
|
-
renders exactly what it rendered before.
|
|
62
|
+
Two steps, and nothing else:
|
|
79
63
|
|
|
80
64
|
```jsx
|
|
81
|
-
|
|
82
|
-
<SignIn />
|
|
65
|
+
import { useSignIn } from '@ciromaciel/auth-react'
|
|
83
66
|
|
|
84
|
-
|
|
85
|
-
<SignIn socialLogin={false} />
|
|
86
|
-
```
|
|
67
|
+
const { requestCode, verifyCode, sending, verifying, error } = useSignIn()
|
|
87
68
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`AuthProvider` already calls that one for you. `startSocialLink()`,
|
|
92
|
-
`unlinkSocialProvider()` and `getLinkedProviders()` manage the connections of an
|
|
93
|
-
account that is already signed in.
|
|
69
|
+
await requestCode(email) // a code goes out by email
|
|
70
|
+
await verifyCode(email, code) // the code becomes a session
|
|
71
|
+
```
|
|
94
72
|
|
|
95
|
-
|
|
73
|
+
Or use the ready-made screen:
|
|
96
74
|
|
|
97
75
|
```jsx
|
|
98
76
|
<SignIn
|
|
99
|
-
authenticatedRedirect="/"
|
|
77
|
+
authenticatedRedirect="/" // where to send someone who already has a session
|
|
100
78
|
onCodeSent={email => notify(`Code sent to ${email}`)}
|
|
101
79
|
onSuccess={(user, { result, redirectHandled }) => notify(`Welcome ${user?.email}`)}
|
|
102
|
-
onError={error => notify(error.message)}
|
|
80
|
+
onError={error => notify(error.message)} // error.code carries the stable identifier
|
|
103
81
|
/>
|
|
104
82
|
```
|
|
105
83
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
84
|
+
- `onSuccess` receives the **user** first; the raw API response is `result`.
|
|
85
|
+
- `redirectHandled` is `true` when a `?redirect=` in the URL was already applied. The component
|
|
86
|
+
honours it by default, validating the destination against the API origin and the current one;
|
|
87
|
+
`redirectOrigins` adds domains and `handleRedirect={false}` hands control back to the app.
|
|
88
|
+
- A wrong code fails with `error.details.attemptsLeft`. Five wrong tries destroy the request and
|
|
89
|
+
the person asks for a new code.
|
|
110
90
|
|
|
111
|
-
###
|
|
91
|
+
### Social sign-in
|
|
92
|
+
|
|
93
|
+
`<SignIn />` renders the provider buttons on its own: `socialLogin` defaults to `'auto'`, which
|
|
94
|
+
shows whatever the application owner enabled and nothing at all when they enabled none.
|
|
95
|
+
|
|
96
|
+
```jsx
|
|
97
|
+
<SignIn /> {/* buttons appear when a provider is live */}
|
|
98
|
+
<SignIn socialLogin={false} /> {/* emailed code only */}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Building your own screen? `getSocialProviders()` lists what is live,
|
|
102
|
+
`startSocialSignIn(provider, { redirect })` leaves for the provider, and `consumeSocialToken()`
|
|
103
|
+
reads the token the callback leaves in the URL fragment — `AuthProvider` already calls that one
|
|
104
|
+
for you. `startSocialLink()`, `unlinkSocialProvider()` and `getLinkedProviders()` manage the
|
|
105
|
+
connections of an account that is already signed in.
|
|
106
|
+
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
### Components
|
|
110
|
+
|
|
111
|
+
| Component | Description |
|
|
112
|
+
| ------------------------- | ------------------------------------------------------------ |
|
|
113
|
+
| `<SignIn />` | The whole way in: asks for the email, then takes the code |
|
|
114
|
+
| `<UserProfile />` | User profile management modal |
|
|
115
|
+
| `<UserInformation />` | User details and account menu |
|
|
116
|
+
| `<SocialButtons />` | Sign-in buttons for the providers the application enabled |
|
|
117
|
+
| `<AuthCard />` | The card shell the screens are built on |
|
|
118
|
+
| `<Wordmark />` | Application wordmark |
|
|
119
|
+
|
|
120
|
+
### Routing
|
|
121
|
+
|
|
122
|
+
| Component | Description |
|
|
123
|
+
| ---------------- | ------------------------------------------------------------- |
|
|
124
|
+
| `<Protect />` | Route wrapper; redirects to `redirectTo` without a session |
|
|
125
|
+
| `<GuestOnly />` | The counterpart: keeps a signed-in person off the login screen |
|
|
126
|
+
|
|
127
|
+
### Conditional rendering
|
|
112
128
|
|
|
113
129
|
| Component | Description |
|
|
114
130
|
| --------------- | -------------------------------------------- |
|
|
115
131
|
| `<SignedIn>` | Renders children only when authenticated |
|
|
116
132
|
| `<SignedOut>` | Renders children only when NOT authenticated |
|
|
117
133
|
| `<AuthLoading>` | Renders children while auth is loading |
|
|
118
|
-
| `<AuthLoaded>` | Renders children
|
|
119
|
-
| `<Protect />` | Protected route wrapper |
|
|
134
|
+
| `<AuthLoaded>` | Renders children once auth has loaded |
|
|
120
135
|
|
|
121
|
-
### Unstyled
|
|
136
|
+
### Unstyled buttons
|
|
122
137
|
|
|
123
138
|
| Component | Description |
|
|
124
139
|
| ------------------- | ------------------------- |
|
|
125
140
|
| `<SignInButton />` | Navigates to sign-in page |
|
|
126
141
|
| `<SignOutButton />` | Signs out the user |
|
|
127
142
|
|
|
128
|
-
|
|
143
|
+
### Hooks
|
|
129
144
|
|
|
130
145
|
```jsx
|
|
131
|
-
const { user, loading, error, isAuthenticated } = useAuth()
|
|
132
|
-
const { user, updateProfile } = useUser()
|
|
133
|
-
const { requestCode, verifyCode, sending, verifying } = useSignIn()
|
|
146
|
+
const { user, loading, error, isAuthenticated, requestCode, verifyCode, signOut } = useAuth()
|
|
147
|
+
const { user, updateProfile, loadingUpdateProfile } = useUser()
|
|
148
|
+
const { requestCode, verifyCode, sending, verifying, error } = useSignIn()
|
|
149
|
+
const { getSession, user, setUser } = useSession()
|
|
150
|
+
const { sessions, listSessions, revokeSession, revokeOtherSessions } = useSessions()
|
|
151
|
+
const loadingStates = useAuthLoading()
|
|
152
|
+
const impersonation = useImpersonation() // the borrowed session, or null
|
|
153
|
+
const logo = useApplicationLogo()
|
|
134
154
|
const signOut = useSignOut()
|
|
135
|
-
|
|
136
|
-
// The two steps, and nothing else:
|
|
137
|
-
await requestCode(email) // a code goes out by email
|
|
138
|
-
await verifyCode(email, code) // the code becomes a session
|
|
155
|
+
const checkToken = useCheckToken()
|
|
139
156
|
```
|
|
140
157
|
|
|
158
|
+
### Direct SDK calls
|
|
159
|
+
|
|
160
|
+
For code outside React, the HTTP layer is exported too: `configure`, `requestCode`, `verifyCode`,
|
|
161
|
+
`pollCode`, `signOut`, `refreshToken`, `getSession`, `listSessions`, `revokeSession`,
|
|
162
|
+
`revokeOtherSessions`, `updateProfile`, `getApplicationInfo`, `isAuthenticated`, `getCurrentUser`,
|
|
163
|
+
`decodeJWT`, `setStoredToken`, and the social helpers listed above.
|
|
164
|
+
|
|
141
165
|
## Features
|
|
142
166
|
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
| Removed | Use instead |
|
|
158
|
-
| ------------------------------------------------------------------ | ---------------------------------------- |
|
|
159
|
-
| `signUp`, `signIn`, `sendMagicLink`, `verifyMagicLink` | `requestCode` + `verifyCode` |
|
|
160
|
-
| `forgotPassword`, `resetPassword`, `changePassword` | — there is no password |
|
|
161
|
-
| `verifyEmail`, `resendVerification` | — presenting the code already verifies |
|
|
162
|
-
| `changeEmail` | the email is the credential |
|
|
163
|
-
| `socialRedirect` | never existed under that name — social sign-in ships as `startSocialSignIn` |
|
|
164
|
-
| `<SignUp>`, `<MagicLink>`, `<MagicLinkCallback>` | `<SignIn>` |
|
|
165
|
-
| `<ForgotPassword>`, `<ResetPassword>`, `<VerifyEmail>` | `<SignIn>` |
|
|
166
|
-
| `<SignUpButton>` | `<SignInButton>` |
|
|
167
|
-
| `useSignUp`, `useMagicLink`, `usePasswordReset` | `useSignIn` |
|
|
168
|
-
| `useEmailVerification` | `useSignIn` |
|
|
169
|
-
| `SignInForm`, `AccountModal`, `ProtectedRoute`, `useProfile` | `SignIn`, `UserProfile`, `Protect`, `useUser` |
|
|
167
|
+
- **Passwordless** — an emailed code, for people and agents alike; no password to leak
|
|
168
|
+
- **JWT sessions** with automatic refresh
|
|
169
|
+
- **JWKS** signature verification via `/.well-known/jwks.json`
|
|
170
|
+
- **Cross-tab sync** — sign out in one tab, every tab follows
|
|
171
|
+
- **Route protection** through `<Protect />` and `<GuestOnly />`
|
|
172
|
+
- **SSR friendly**
|
|
173
|
+
|
|
174
|
+
## Links
|
|
175
|
+
|
|
176
|
+
- [Website](https://ciromaciel.click)
|
|
177
|
+
- [Package on npm](https://www.npmjs.com/package/@ciromaciel/auth-react)
|
|
178
|
+
|
|
179
|
+
MIT © [Ciro Cesar Maciel](https://ciromaciel.click)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciromaciel/auth-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Auth SDK for React
|
|
5
|
+
"description": "Auth SDK for React: JWT sessions, passwordless sign-in, and ready-made Mantine screens.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
@@ -68,11 +68,6 @@
|
|
|
68
68
|
"semantic-release": "^24.2.5",
|
|
69
69
|
"zustand": "^5.0.9"
|
|
70
70
|
},
|
|
71
|
-
"repository": {
|
|
72
|
-
"type": "git",
|
|
73
|
-
"url": "https://github.com/riligar-infrastructure/auth",
|
|
74
|
-
"directory": "packages/auth-react"
|
|
75
|
-
},
|
|
76
71
|
"publishConfig": {
|
|
77
72
|
"access": "public"
|
|
78
73
|
},
|
|
@@ -83,6 +78,10 @@
|
|
|
83
78
|
"sdk",
|
|
84
79
|
"riligar"
|
|
85
80
|
],
|
|
86
|
-
"author": "
|
|
87
|
-
"license": "
|
|
81
|
+
"author": "Ciro Cesar Maciel <maciel.ciro@icloud.com> (https://ciromaciel.click)",
|
|
82
|
+
"license": "MIT",
|
|
83
|
+
"homepage": "https://ciromaciel.click",
|
|
84
|
+
"bugs": {
|
|
85
|
+
"url": "https://ciromaciel.click"
|
|
86
|
+
}
|
|
88
87
|
}
|