@rudderjs/auth 3.1.1 → 3.2.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/README.md +29 -11
- package/package.json +6 -6
- package/views/react/ForgotPassword.tsx +13 -14
- package/views/react/Login.tsx +15 -16
- package/views/react/Register.tsx +16 -17
- package/views/react/ResetPassword.tsx +25 -26
package/README.md
CHANGED
|
@@ -65,9 +65,16 @@ Route.get('/profile', async (req) => {
|
|
|
65
65
|
})
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
**No per-route wiring needed on web routes.** `authProvider()` auto-installs
|
|
69
|
+
`AuthMiddleware` on the `web` route group during `boot()`, so every request
|
|
70
|
+
matched by `withRouting({ web })` has the auth context populated before your
|
|
71
|
+
handler runs.
|
|
72
|
+
|
|
73
|
+
**API routes stay stateless.** `AuthMiddleware` does not run on the `api` group
|
|
74
|
+
by default — `req.user` will be `undefined`, and `Auth.user()` returns `null`.
|
|
75
|
+
For token-based API auth, reach for [`@rudderjs/passport`](../passport):
|
|
76
|
+
`[RequireBearer(), scope('read')]`. Or mount `AuthMiddleware('api')` per-route
|
|
77
|
+
with a token guard if you've wired one.
|
|
71
78
|
|
|
72
79
|
### Login / logout
|
|
73
80
|
|
|
@@ -100,21 +107,32 @@ Route.get('/login', showLoginPage, [RequireGuest('/')])
|
|
|
100
107
|
import { AuthMiddleware } from '@rudderjs/auth'
|
|
101
108
|
```
|
|
102
109
|
|
|
103
|
-
**You don't normally attach this.**
|
|
104
|
-
|
|
105
|
-
on every
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
**You don't normally attach this on web routes.** `authProvider()` already
|
|
111
|
+
installs `AuthMiddleware()` on the `web` route group, so `req.user` and `auth()`
|
|
112
|
+
work automatically on every web request. Reach for it manually in two cases:
|
|
113
|
+
|
|
114
|
+
**1. A non-default guard on a specific web route** — the RudderJS equivalent
|
|
115
|
+
of Laravel's `->middleware('auth:api')`:
|
|
108
116
|
|
|
109
117
|
```ts
|
|
110
|
-
Route.get('/
|
|
118
|
+
Route.get('/admin/stats', handler, [
|
|
111
119
|
AuthMiddleware('api'), // populate req.user using the 'api' guard
|
|
112
120
|
RequireAuth('api'), // 401 if not authenticated against 'api'
|
|
113
121
|
])
|
|
114
122
|
```
|
|
115
123
|
|
|
116
|
-
|
|
117
|
-
|
|
124
|
+
**2. Opting an API route into a token-backed guard** — API routes are
|
|
125
|
+
stateless by default, so wire the guard per-route:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
Route.get('/api/admin/stats', handler, [
|
|
129
|
+
AuthMiddleware('api'),
|
|
130
|
+
RequireAuth('api'),
|
|
131
|
+
])
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
On web routes, you can forget `AuthMiddleware` exists — use `auth()`, `Auth`,
|
|
135
|
+
or `req.user` directly.
|
|
118
136
|
|
|
119
137
|
### Authenticatable Contract
|
|
120
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rudderjs/auth",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"rudderjs": {
|
|
5
5
|
"provider": "AuthProvider",
|
|
6
6
|
"stage": "infrastructure",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"./package.json": "./package.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@rudderjs/
|
|
48
|
-
"@rudderjs/
|
|
47
|
+
"@rudderjs/core": "0.1.1",
|
|
48
|
+
"@rudderjs/contracts": "0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@rudderjs/router": "0.3.0",
|
|
52
|
-
"@rudderjs/view": "0.0.3",
|
|
53
51
|
"@rudderjs/hash": "0.0.7",
|
|
54
|
-
"@rudderjs/session": "0.1.1"
|
|
52
|
+
"@rudderjs/session": "0.1.1",
|
|
53
|
+
"@rudderjs/view": "0.0.3",
|
|
54
|
+
"@rudderjs/router": "0.3.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
57
|
"@rudderjs/hash": {
|
|
@@ -44,31 +44,30 @@ export default function ForgotPassword(props: ForgotPasswordProps) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
|
-
<div className="
|
|
48
|
-
<div className="
|
|
49
|
-
<div className="
|
|
50
|
-
<h1 className="
|
|
51
|
-
<p className="
|
|
47
|
+
<div className="auth-wrap">
|
|
48
|
+
<div className="auth-card">
|
|
49
|
+
<div className="auth-head">
|
|
50
|
+
<h1 className="heading-lg">Forgot password</h1>
|
|
51
|
+
<p className="muted">Enter your email to receive a reset link</p>
|
|
52
52
|
</div>
|
|
53
|
-
<form onSubmit={handleSubmit} className="
|
|
54
|
-
{error && <p className="
|
|
55
|
-
{success && <p className="
|
|
53
|
+
<form onSubmit={handleSubmit} className="form-card">
|
|
54
|
+
{error && <p className="form-error">{error}</p>}
|
|
55
|
+
{success && <p className="form-success">{success}</p>}
|
|
56
56
|
<div>
|
|
57
|
-
<label className="
|
|
57
|
+
<label className="form-label" htmlFor="email">Email</label>
|
|
58
58
|
<input
|
|
59
59
|
id="email" type="email" placeholder="you@example.com"
|
|
60
60
|
value={email} onChange={e => setEmail(e.currentTarget.value)}
|
|
61
61
|
required autoComplete="email"
|
|
62
|
-
className="
|
|
62
|
+
className="form-input"
|
|
63
63
|
/>
|
|
64
64
|
</div>
|
|
65
|
-
<button type="submit" disabled={loading}
|
|
66
|
-
className="w-full rounded-md bg-black px-4 py-2 text-sm font-medium text-white hover:bg-black/90 disabled:opacity-50">
|
|
65
|
+
<button type="submit" disabled={loading} className="form-submit">
|
|
67
66
|
{loading ? 'Sending...' : 'Send reset link'}
|
|
68
67
|
</button>
|
|
69
|
-
<p className="
|
|
68
|
+
<p className="auth-head muted">
|
|
70
69
|
Remember your password?{' '}
|
|
71
|
-
<a href={loginUrl} className="
|
|
70
|
+
<a href={loginUrl} className="auth-link">Sign in</a>
|
|
72
71
|
</p>
|
|
73
72
|
</form>
|
|
74
73
|
</div>
|
package/views/react/Login.tsx
CHANGED
|
@@ -47,39 +47,38 @@ export default function Login(props: LoginProps) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
<div className="
|
|
51
|
-
<div className="
|
|
52
|
-
<div className="
|
|
53
|
-
<h1 className="
|
|
54
|
-
<p className="
|
|
50
|
+
<div className="auth-wrap">
|
|
51
|
+
<div className="auth-card">
|
|
52
|
+
<div className="auth-head">
|
|
53
|
+
<h1 className="heading-lg">Welcome back</h1>
|
|
54
|
+
<p className="muted">Sign in to your account</p>
|
|
55
55
|
</div>
|
|
56
|
-
<form onSubmit={handleSubmit} className="
|
|
57
|
-
{error && <p className="
|
|
56
|
+
<form onSubmit={handleSubmit} className="form-card">
|
|
57
|
+
{error && <p className="form-error">{error}</p>}
|
|
58
58
|
<div>
|
|
59
|
-
<label className="
|
|
59
|
+
<label className="form-label" htmlFor="email">Email</label>
|
|
60
60
|
<input
|
|
61
61
|
id="email" type="email" placeholder="you@example.com"
|
|
62
62
|
value={email} onChange={e => setEmail(e.currentTarget.value)}
|
|
63
63
|
required autoComplete="email"
|
|
64
|
-
className="
|
|
64
|
+
className="form-input"
|
|
65
65
|
/>
|
|
66
66
|
</div>
|
|
67
67
|
<div>
|
|
68
|
-
<label className="
|
|
68
|
+
<label className="form-label" htmlFor="password">Password</label>
|
|
69
69
|
<input
|
|
70
70
|
id="password" type="password" placeholder="••••••••"
|
|
71
71
|
value={password} onChange={e => setPassword(e.currentTarget.value)}
|
|
72
72
|
required autoComplete="current-password"
|
|
73
|
-
className="
|
|
73
|
+
className="form-input"
|
|
74
74
|
/>
|
|
75
75
|
</div>
|
|
76
|
-
<button type="submit" disabled={loading}
|
|
77
|
-
className="w-full rounded-md bg-black px-4 py-2 text-sm font-medium text-white hover:bg-black/90 disabled:opacity-50">
|
|
76
|
+
<button type="submit" disabled={loading} className="form-submit">
|
|
78
77
|
{loading ? 'Signing in…' : 'Sign in'}
|
|
79
78
|
</button>
|
|
80
|
-
<div className="
|
|
81
|
-
<a href={forgotPasswordUrl} className="
|
|
82
|
-
<a href={registerUrl} className="
|
|
79
|
+
<div className="form-link-row">
|
|
80
|
+
<a href={forgotPasswordUrl} className="auth-link">Forgot password?</a>
|
|
81
|
+
<a href={registerUrl} className="auth-link">Register</a>
|
|
83
82
|
</div>
|
|
84
83
|
</form>
|
|
85
84
|
</div>
|
package/views/react/Register.tsx
CHANGED
|
@@ -41,48 +41,47 @@ export default function Register(props: RegisterProps) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<div className="
|
|
45
|
-
<div className="
|
|
46
|
-
<div className="
|
|
47
|
-
<h1 className="
|
|
48
|
-
<p className="
|
|
44
|
+
<div className="auth-wrap">
|
|
45
|
+
<div className="auth-card">
|
|
46
|
+
<div className="auth-head">
|
|
47
|
+
<h1 className="heading-lg">Create an account</h1>
|
|
48
|
+
<p className="muted">Get started in seconds</p>
|
|
49
49
|
</div>
|
|
50
|
-
<form onSubmit={handleSubmit} className="
|
|
51
|
-
{error && <p className="
|
|
50
|
+
<form onSubmit={handleSubmit} className="form-card">
|
|
51
|
+
{error && <p className="form-error">{error}</p>}
|
|
52
52
|
<div>
|
|
53
|
-
<label className="
|
|
53
|
+
<label className="form-label" htmlFor="name">Name</label>
|
|
54
54
|
<input
|
|
55
55
|
id="name" type="text" placeholder="Alice Smith"
|
|
56
56
|
value={name} onChange={e => setName(e.currentTarget.value)}
|
|
57
57
|
required autoComplete="name"
|
|
58
|
-
className="
|
|
58
|
+
className="form-input"
|
|
59
59
|
/>
|
|
60
60
|
</div>
|
|
61
61
|
<div>
|
|
62
|
-
<label className="
|
|
62
|
+
<label className="form-label" htmlFor="email">Email</label>
|
|
63
63
|
<input
|
|
64
64
|
id="email" type="email" placeholder="you@example.com"
|
|
65
65
|
value={email} onChange={e => setEmail(e.currentTarget.value)}
|
|
66
66
|
required autoComplete="email"
|
|
67
|
-
className="
|
|
67
|
+
className="form-input"
|
|
68
68
|
/>
|
|
69
69
|
</div>
|
|
70
70
|
<div>
|
|
71
|
-
<label className="
|
|
71
|
+
<label className="form-label" htmlFor="password">Password</label>
|
|
72
72
|
<input
|
|
73
73
|
id="password" type="password" placeholder="••••••••"
|
|
74
74
|
value={password} onChange={e => setPassword(e.currentTarget.value)}
|
|
75
75
|
required autoComplete="new-password" minLength={8}
|
|
76
|
-
className="
|
|
76
|
+
className="form-input"
|
|
77
77
|
/>
|
|
78
78
|
</div>
|
|
79
|
-
<button type="submit" disabled={loading}
|
|
80
|
-
className="w-full rounded-md bg-black px-4 py-2 text-sm font-medium text-white hover:bg-black/90 disabled:opacity-50">
|
|
79
|
+
<button type="submit" disabled={loading} className="form-submit">
|
|
81
80
|
{loading ? 'Creating account…' : 'Create account'}
|
|
82
81
|
</button>
|
|
83
|
-
<p className="
|
|
82
|
+
<p className="auth-head muted">
|
|
84
83
|
Already have an account?{' '}
|
|
85
|
-
<a href={loginUrl} className="
|
|
84
|
+
<a href={loginUrl} className="auth-link">Sign in</a>
|
|
86
85
|
</p>
|
|
87
86
|
</form>
|
|
88
87
|
</div>
|
|
@@ -62,20 +62,20 @@ export default function ResetPassword(props: ResetPasswordProps) {
|
|
|
62
62
|
|
|
63
63
|
if (!mounted) {
|
|
64
64
|
return (
|
|
65
|
-
<div className="
|
|
66
|
-
<div className="
|
|
65
|
+
<div className="auth-wrap">
|
|
66
|
+
<div className="muted">Loading...</div>
|
|
67
67
|
</div>
|
|
68
68
|
)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
if (!token) {
|
|
72
72
|
return (
|
|
73
|
-
<div className="
|
|
74
|
-
<div className="
|
|
75
|
-
<div className="
|
|
76
|
-
<p className="
|
|
77
|
-
<p className="
|
|
78
|
-
<a href={forgotPasswordUrl} className="
|
|
73
|
+
<div className="auth-wrap">
|
|
74
|
+
<div className="auth-card">
|
|
75
|
+
<div className="form-card">
|
|
76
|
+
<p className="form-error">Missing reset token.</p>
|
|
77
|
+
<p className="auth-head muted">
|
|
78
|
+
<a href={forgotPasswordUrl} className="auth-link">Request a new reset link</a>
|
|
79
79
|
</p>
|
|
80
80
|
</div>
|
|
81
81
|
</div>
|
|
@@ -84,44 +84,43 @@ export default function ResetPassword(props: ResetPasswordProps) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
return (
|
|
87
|
-
<div className="
|
|
88
|
-
<div className="
|
|
89
|
-
<div className="
|
|
90
|
-
<h1 className="
|
|
91
|
-
<p className="
|
|
87
|
+
<div className="auth-wrap">
|
|
88
|
+
<div className="auth-card">
|
|
89
|
+
<div className="auth-head">
|
|
90
|
+
<h1 className="heading-lg">Reset password</h1>
|
|
91
|
+
<p className="muted">Enter your new password</p>
|
|
92
92
|
</div>
|
|
93
|
-
<form onSubmit={handleSubmit} className="
|
|
94
|
-
{error && <p className="
|
|
93
|
+
<form onSubmit={handleSubmit} className="form-card">
|
|
94
|
+
{error && <p className="form-error">{error}</p>}
|
|
95
95
|
{success && (
|
|
96
|
-
|
|
97
|
-
<p className="
|
|
98
|
-
<p className="
|
|
99
|
-
<a href={loginUrl} className="
|
|
96
|
+
<>
|
|
97
|
+
<p className="form-success">{success}</p>
|
|
98
|
+
<p className="auth-head muted">
|
|
99
|
+
<a href={loginUrl} className="auth-link">Sign in</a>
|
|
100
100
|
</p>
|
|
101
|
-
|
|
101
|
+
</>
|
|
102
102
|
)}
|
|
103
103
|
{!success && (
|
|
104
104
|
<>
|
|
105
105
|
<div>
|
|
106
|
-
<label className="
|
|
106
|
+
<label className="form-label" htmlFor="password">New password</label>
|
|
107
107
|
<input
|
|
108
108
|
id="password" type="password" placeholder="••••••••"
|
|
109
109
|
value={password} onChange={e => setPassword(e.currentTarget.value)}
|
|
110
110
|
required minLength={8} autoComplete="new-password"
|
|
111
|
-
className="
|
|
111
|
+
className="form-input"
|
|
112
112
|
/>
|
|
113
113
|
</div>
|
|
114
114
|
<div>
|
|
115
|
-
<label className="
|
|
115
|
+
<label className="form-label" htmlFor="confirm-password">Confirm password</label>
|
|
116
116
|
<input
|
|
117
117
|
id="confirm-password" type="password" placeholder="••••••••"
|
|
118
118
|
value={confirmPassword} onChange={e => setConfirm(e.currentTarget.value)}
|
|
119
119
|
required minLength={8} autoComplete="new-password"
|
|
120
|
-
className="
|
|
120
|
+
className="form-input"
|
|
121
121
|
/>
|
|
122
122
|
</div>
|
|
123
|
-
<button type="submit" disabled={loading}
|
|
124
|
-
className="w-full rounded-md bg-black px-4 py-2 text-sm font-medium text-white hover:bg-black/90 disabled:opacity-50">
|
|
123
|
+
<button type="submit" disabled={loading} className="form-submit">
|
|
125
124
|
{loading ? 'Resetting...' : 'Reset password'}
|
|
126
125
|
</button>
|
|
127
126
|
</>
|