sekisho 0.1.0 → 0.1.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 +204 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<h1 align="center">⛩️ sekisho</h1>
|
|
2
|
+
<p align="center"><sup>(関所, <em>historical checkpoint for travel and security</em> in Japanese)</sup></p>
|
|
3
|
+
<p align="center">Authentication and Access Control for any React app</p>
|
|
4
|
+
|
|
5
|
+
----
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
### Full example
|
|
10
|
+
|
|
11
|
+
See the [example-nextjs-app](../../packages/example-nextjs-app).
|
|
12
|
+
|
|
13
|
+
### Simple setup
|
|
14
|
+
|
|
15
|
+
Wrap your app with `SekishoProvider` with options:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
// app/providers.tsx
|
|
19
|
+
|
|
20
|
+
// here we create a dedicated file, as we want to make this file a client component in Next.js App Router
|
|
21
|
+
//
|
|
22
|
+
// if you don't use the Next.js App Router, you can just wrap your app's entrypoint with `SekishoProvider`
|
|
23
|
+
// directly without creating a separate file
|
|
24
|
+
|
|
25
|
+
'use client';
|
|
26
|
+
|
|
27
|
+
import { SekishoProvider } from 'sekisho';
|
|
28
|
+
import { useRouter } from 'next/navigation';
|
|
29
|
+
|
|
30
|
+
export function Providers({ children }: React.PropsWithChildren) {
|
|
31
|
+
const router = useRouter();
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<SekishoProvider
|
|
35
|
+
onNeedLogin={() => {
|
|
36
|
+
// Tell Sekisho what to do when the authentication is required.
|
|
37
|
+
router.push('/login');
|
|
38
|
+
// Sekisho can work with any router or navigation library.
|
|
39
|
+
// You can also call `navigate` from React Router's `useNavigate` here:
|
|
40
|
+
// navigate('/login');
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
</SekishoProvider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// app/layout.tsx
|
|
51
|
+
import { Providers } from './providers';
|
|
52
|
+
|
|
53
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
54
|
+
return (
|
|
55
|
+
<html lang="en">
|
|
56
|
+
<body>
|
|
57
|
+
<Providers>{children}</Providers>
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
By default, `SekishoProvider` already includes `SekishoErrorBoundary` that will catch any `NotAuthenticatedError` thrown by `needLogin()` in the subtree. But if you have special error handling needs in certain parts of your app, you can also always import `SekishoErrorBoundary` directly to wrap those parts:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { SekishoErrorBoundary } from 'sekisho';
|
|
68
|
+
|
|
69
|
+
function SomePartOfApp() {
|
|
70
|
+
return (
|
|
71
|
+
<SekishoErrorBoundary>
|
|
72
|
+
{/* ... */}
|
|
73
|
+
</SekishoErrorBoundary>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
And if you are using Next.js App Router and `error.tsx` file, due to Next.js layout, page, and error boundary heirarchy, you will also need to wrap the `error.tsx` with `SekishoErrorWrapper`:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// app/error.tsx
|
|
82
|
+
'use client';
|
|
83
|
+
|
|
84
|
+
import { SekishoErrorWrapper } from 'sekisho';
|
|
85
|
+
|
|
86
|
+
export default function ErrorPage({ error, reset }) {
|
|
87
|
+
return (
|
|
88
|
+
<SekishoErrorWrapper error={error}>
|
|
89
|
+
{/* Your existing error UI goes in here */}
|
|
90
|
+
</SekishoErrorWrapper>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> `SekishoErrorWrapper` is actually used by `SekishoErrorBoundary` internally, containing all the core logic.
|
|
96
|
+
|
|
97
|
+
### Triggering a login redirect
|
|
98
|
+
|
|
99
|
+
Call `needLogin()` anywhere during the React render phase.
|
|
100
|
+
|
|
101
|
+
Right now, you can't call `needLogin()` within an event handler or `useEffect`, because it won't be caught by the React error boundary mechanism. We will be implementing this in a future version.
|
|
102
|
+
|
|
103
|
+
**In a client component** (e.g. when session state is absent):
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { needLogin } from 'sekisho';
|
|
107
|
+
|
|
108
|
+
function Dashboard() {
|
|
109
|
+
const session = useAuthSession();
|
|
110
|
+
|
|
111
|
+
if (!session) {
|
|
112
|
+
needLogin('No active session');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return <div>Welcome, {session.username}</div>;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**In a [SWR](https://swr.vercel.app) middleware** (e.g. when an API response carries a known auth error):
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { needLogin } from 'sekisho';
|
|
123
|
+
import type { Middleware } from 'swr';
|
|
124
|
+
|
|
125
|
+
export const requireAuthMiddleware: Middleware = (useSWRNext) => (key, fetcher, config) => {
|
|
126
|
+
const swr = useSWRNext(key, fetcher, config);
|
|
127
|
+
if (swr.error && isApiAuthError(swr.error)) {
|
|
128
|
+
needLogin(swr.error.message);
|
|
129
|
+
}
|
|
130
|
+
return swr;
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Explanation
|
|
135
|
+
|
|
136
|
+
Sekisho is built on top of React's error boundaries. When you call `needLogin()` within the React render phase, it throws a special `NotAuthenticatedError`. React will then bubble the error up to the nearest React error boundary, and that's when Sekisho's error boundary went into action: it checks if the error is a `NotAuthenticatedError`, and if so, it calls the `onNeedLogin` callback you provided to `SekishoProvider`, and re-throws the error if it is not.
|
|
137
|
+
|
|
138
|
+
This way, you can centralize your authentication logic and keep it separate from your UI components.
|
|
139
|
+
|
|
140
|
+
Also, with the `SekishoErrorWrapper` / `SekishoErrorBoundary`, you can create protected routes and unprotected routes more easily with any React apps:
|
|
141
|
+
|
|
142
|
+
**Next.js App Router**
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
app/
|
|
146
|
+
├── (protected)/ ← all protected routes goes under here
|
|
147
|
+
│ ├── layout.tsx ← wrap children with <SekishoProvider> here
|
|
148
|
+
│ ├── error.tsx ← wrap with <SekishoErrorWrapper> here
|
|
149
|
+
│ └── page.tsx ← homepage, where you call needLogin() when authentication is needed
|
|
150
|
+
├── (unprotected)/ ← all unprotected routes goes under here
|
|
151
|
+
│ └── login/
|
|
152
|
+
│ └── page.tsx
|
|
153
|
+
└── layout.tsx ← root layout
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**React Router**
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
const router = createBrowserRouter([
|
|
160
|
+
{
|
|
161
|
+
component() {
|
|
162
|
+
return (
|
|
163
|
+
<RootLayout>
|
|
164
|
+
<SekishoProvider onNeedLogin={() => navigate('/login')}>
|
|
165
|
+
<Outlet />
|
|
166
|
+
</SekishoProvider>
|
|
167
|
+
</RootLayout>
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
children: [
|
|
171
|
+
{
|
|
172
|
+
component() {
|
|
173
|
+
return (
|
|
174
|
+
<SekishoErrorBoundary>
|
|
175
|
+
<Outlet />
|
|
176
|
+
</SekishoErrorBoundary>
|
|
177
|
+
);
|
|
178
|
+
},
|
|
179
|
+
children: [
|
|
180
|
+
// protected routes goes here
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
// unprotected routes goes here
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
]);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
[MIT](LICENSE)
|
|
192
|
+
|
|
193
|
+
----
|
|
194
|
+
|
|
195
|
+
**sekisho** © [Sukka](https://github.com/SukkaW), Released under the [MIT](./LICENSE) License.
|
|
196
|
+
Authored and maintained by Sukka with help from contributors ([list](https://github.com/SukkaW/sekisho/graphs/contributors)).
|
|
197
|
+
|
|
198
|
+
> [Personal Website](https://skk.moe) · [Blog](https://blog.skk.moe) · GitHub [@SukkaW](https://github.com/SukkaW) · Telegram Channel [@SukkaChannel](https://t.me/SukkaChannel) · Mastodon [@sukka@acg.mn](https://acg.mn/@sukka) · Twitter [@isukkaw](https://twitter.com/isukkaw) · BlueSky [@skk.moe](https://bsky.app/profile/skk.moe)
|
|
199
|
+
|
|
200
|
+
<p align="center">
|
|
201
|
+
<a href="https://github.com/sponsors/SukkaW/">
|
|
202
|
+
<img src="https://sponsor.cdn.skk.moe/sponsors.svg"/>
|
|
203
|
+
</a>
|
|
204
|
+
</p>
|