@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 CHANGED
@@ -65,9 +65,16 @@ Route.get('/profile', async (req) => {
65
65
  })
66
66
  ```
67
67
 
68
- None of these require attaching any middleware per-route. The `authProvider()`
69
- service provider installs `AuthMiddleware` as a global router middleware at
70
- boot, so every request has the auth context ready before your handler runs.
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.** The `authProvider()` service provider
104
- already installs `AuthMiddleware()` globally, so `req.user` and `auth()` work
105
- on every route without wiring. The only time to reach for it manually is to
106
- run a **non-default guard** on a specific route — the RudderJS equivalent of
107
- Laravel's `->middleware('auth:api')`:
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('/api/admin/stats', handler, [
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
- In normal app code you can forget `AuthMiddleware` existsuse `auth()`,
117
- `Auth`, or `req.user` directly.
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.1.1",
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/contracts": "0.1.0",
48
- "@rudderjs/core": "0.1.1"
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="flex min-h-svh items-center justify-center p-4">
48
- <div className="w-full max-w-sm space-y-6">
49
- <div className="text-center">
50
- <h1 className="text-2xl font-bold">Forgot password</h1>
51
- <p className="text-sm text-gray-500 mt-1">Enter your email to receive a reset link</p>
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="space-y-4 rounded-lg border p-6 shadow-sm">
54
- {error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
55
- {success && <p className="rounded-md bg-green-50 px-3 py-2 text-sm text-green-600">{success}</p>}
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="block text-sm font-medium mb-1" htmlFor="email">Email</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
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="text-center text-sm text-gray-500">
68
+ <p className="auth-head muted">
70
69
  Remember your password?{' '}
71
- <a href={loginUrl} className="underline hover:text-black">Sign in</a>
70
+ <a href={loginUrl} className="auth-link">Sign in</a>
72
71
  </p>
73
72
  </form>
74
73
  </div>
@@ -47,39 +47,38 @@ export default function Login(props: LoginProps) {
47
47
  }
48
48
 
49
49
  return (
50
- <div className="flex min-h-svh items-center justify-center p-4">
51
- <div className="w-full max-w-sm space-y-6">
52
- <div className="text-center">
53
- <h1 className="text-2xl font-bold">Welcome back</h1>
54
- <p className="text-sm text-gray-500 mt-1">Sign in to your account</p>
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="space-y-4 rounded-lg border p-6 shadow-sm">
57
- {error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
56
+ <form onSubmit={handleSubmit} className="form-card">
57
+ {error && <p className="form-error">{error}</p>}
58
58
  <div>
59
- <label className="block text-sm font-medium mb-1" htmlFor="email">Email</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
64
+ className="form-input"
65
65
  />
66
66
  </div>
67
67
  <div>
68
- <label className="block text-sm font-medium mb-1" htmlFor="password">Password</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
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="flex items-center justify-between text-sm text-gray-500">
81
- <a href={forgotPasswordUrl} className="underline hover:text-black">Forgot password?</a>
82
- <a href={registerUrl} className="underline hover:text-black">Register</a>
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>
@@ -41,48 +41,47 @@ export default function Register(props: RegisterProps) {
41
41
  }
42
42
 
43
43
  return (
44
- <div className="flex min-h-svh items-center justify-center p-4">
45
- <div className="w-full max-w-sm space-y-6">
46
- <div className="text-center">
47
- <h1 className="text-2xl font-bold">Create an account</h1>
48
- <p className="text-sm text-gray-500 mt-1">Get started in seconds</p>
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="space-y-4 rounded-lg border p-6 shadow-sm">
51
- {error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
50
+ <form onSubmit={handleSubmit} className="form-card">
51
+ {error && <p className="form-error">{error}</p>}
52
52
  <div>
53
- <label className="block text-sm font-medium mb-1" htmlFor="name">Name</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
58
+ className="form-input"
59
59
  />
60
60
  </div>
61
61
  <div>
62
- <label className="block text-sm font-medium mb-1" htmlFor="email">Email</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
67
+ className="form-input"
68
68
  />
69
69
  </div>
70
70
  <div>
71
- <label className="block text-sm font-medium mb-1" htmlFor="password">Password</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
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="text-center text-sm text-gray-500">
82
+ <p className="auth-head muted">
84
83
  Already have an account?{' '}
85
- <a href={loginUrl} className="underline hover:text-black">Sign in</a>
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="flex min-h-svh items-center justify-center p-4">
66
- <div className="text-sm text-gray-500">Loading...</div>
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="flex min-h-svh items-center justify-center p-4">
74
- <div className="w-full max-w-sm space-y-6">
75
- <div className="space-y-4 rounded-lg border p-6 shadow-sm">
76
- <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">Missing reset token.</p>
77
- <p className="text-center text-sm text-gray-500">
78
- <a href={forgotPasswordUrl} className="underline hover:text-black">Request a new reset link</a>
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="flex min-h-svh items-center justify-center p-4">
88
- <div className="w-full max-w-sm space-y-6">
89
- <div className="text-center">
90
- <h1 className="text-2xl font-bold">Reset password</h1>
91
- <p className="text-sm text-gray-500 mt-1">Enter your new password</p>
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="space-y-4 rounded-lg border p-6 shadow-sm">
94
- {error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
93
+ <form onSubmit={handleSubmit} className="form-card">
94
+ {error && <p className="form-error">{error}</p>}
95
95
  {success && (
96
- <div className="space-y-2">
97
- <p className="rounded-md bg-green-50 px-3 py-2 text-sm text-green-600">{success}</p>
98
- <p className="text-center text-sm text-gray-500">
99
- <a href={loginUrl} className="underline hover:text-black">Sign in</a>
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
- </div>
101
+ </>
102
102
  )}
103
103
  {!success && (
104
104
  <>
105
105
  <div>
106
- <label className="block text-sm font-medium mb-1" htmlFor="password">New password</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
111
+ className="form-input"
112
112
  />
113
113
  </div>
114
114
  <div>
115
- <label className="block text-sm font-medium mb-1" htmlFor="confirm-password">Confirm password</label>
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="w-full rounded-md border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-black"
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
  </>