|
| 1 | +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; |
| 2 | +import { generateStandardSecret } from '@logto/shared/universal'; |
| 3 | +import Steps from '@/mdx-components/Steps'; |
| 4 | +import Step from '@/mdx-components/Step'; |
| 5 | +import Checkpoint from '../../fragments/_checkpoint.md'; |
| 6 | +import RedirectUrisWeb, { defaultBaseUrl } from '../../fragments/_redirect-uris-web.mdx'; |
| 7 | + |
| 8 | +<Steps> |
| 9 | + |
| 10 | +<Step |
| 11 | + title="Installation" |
| 12 | + subtitle="Install Logto SDK" |
| 13 | +> |
| 14 | + |
| 15 | +<NpmLikeInstallation packageName="@logto/remix" /> |
| 16 | + |
| 17 | +</Step> |
| 18 | + |
| 19 | +<Step |
| 20 | + title="Init LogtoClient" |
| 21 | +> |
| 22 | + |
| 23 | +Before initializing the SDK, we have to create a `SessionStorage` instance which takes care of the session persistence. In our case, we want to use a cookie-based session: |
| 24 | + |
| 25 | +<Code title="services/auth.server.ts" className="language-ts"> |
| 26 | + {`import { createCookieSessionStorage } from '@remix-run/node'; |
| 27 | +import { makeLogtoRemix } from '@logto/remix'; |
| 28 | +
|
| 29 | +const sessionStorage = createCookieSessionStorage({ |
| 30 | + cookie: { |
| 31 | + name: 'logto-session', |
| 32 | + maxAge: 14 * 24 * 60 * 60, |
| 33 | + secrets: '${generateStandardSecret()}', // Auto-generated 32 digit secret |
| 34 | + }, |
| 35 | +}); |
| 36 | +
|
| 37 | +export const logto = makeLogtoRemix( |
| 38 | + { |
| 39 | + endpoint: '${props.endpoint}', |
| 40 | + appId: '${props.app.id}', |
| 41 | + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', |
| 42 | + baseUrl: '${defaultBaseUrl}', // Change to your own base URL |
| 43 | + }, |
| 44 | + { sessionStorage } |
| 45 | +);`} |
| 46 | +</Code> |
| 47 | + |
| 48 | +</Step> |
| 49 | + |
| 50 | +<Step |
| 51 | + title="Configure redirect URIs" |
| 52 | + subtitle="2 URIs" |
| 53 | +> |
| 54 | + |
| 55 | +<RedirectUrisWeb defaultRedirectUri={`${defaultBaseUrl}api/logto/callback`} /> |
| 56 | + |
| 57 | +</Step> |
| 58 | + |
| 59 | +<Step title="Mounting authentication routes"> |
| 60 | + |
| 61 | +The SDK ships with a convenient function that mounts the authentication routes: sign-in, sign-in callback and the sign-out route. Create a file `routes/api.logto.$action.ts` |
| 62 | + |
| 63 | +```ts title="routes/api.logto.$action.ts" |
| 64 | +import { logto } from '../services/auth.server'; |
| 65 | + |
| 66 | +export const loader = logto.handleAuthRoutes({ |
| 67 | + 'sign-in': { |
| 68 | + path: '/api/logto/sign-in', |
| 69 | + redirectBackTo: '/api/logto/callback', |
| 70 | + }, |
| 71 | + 'sign-in-callback': { |
| 72 | + path: '/api/logto/callback', |
| 73 | + redirectBackTo: '/', |
| 74 | + }, |
| 75 | + 'sign-out': { |
| 76 | + path: '/api/logto/sign-out', |
| 77 | + redirectBackTo: '/', |
| 78 | + }, |
| 79 | + 'sign-up': { |
| 80 | + path: '/api/logto/sign-up', |
| 81 | + redirectBackTo: '/api/logto/callback', |
| 82 | + }, |
| 83 | +}); |
| 84 | +``` |
| 85 | + |
| 86 | +As you can see, the mount process is configurable and you can adjust it for your particular route structure. The whole URL path structure can be customized via the passed configuration object. |
| 87 | + |
| 88 | +When mounting the routes as described above, you can navigate your browser to `/api/logto/sign-in` and you should be redirected to your Logto instance where you have to authenticate then. |
| 89 | + |
| 90 | +</Step> |
| 91 | + |
| 92 | +<Step |
| 93 | + title="Implement sign-in and sign-out" |
| 94 | +> |
| 95 | + |
| 96 | +We have prepared the authentication routes, now let's implement the sign-in and sign-out buttons in your home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `loader` to fetch authentication status from Logto client. |
| 97 | + |
| 98 | +```tsx title="/app/routes/_index.tsx" |
| 99 | +import { type LogtoContext } from '@logto/remix'; |
| 100 | +import { type LoaderFunction } from '@remix-run/node'; |
| 101 | +import { json, Link, useLoaderData } from '@remix-run/react'; |
| 102 | + |
| 103 | +import { logto } from '../services/auth.server'; |
| 104 | + |
| 105 | +type LoaderResponse = { |
| 106 | + readonly context: LogtoContext; |
| 107 | +}; |
| 108 | + |
| 109 | +export const loader: LoaderFunction = async ({ request }) => { |
| 110 | + const context = await logto.getContext({ getAccessToken: false })(request); |
| 111 | + |
| 112 | + return json<LoaderResponse>({ context }); |
| 113 | +}; |
| 114 | + |
| 115 | +const Home = () => { |
| 116 | + const { context } = useLoaderData<LoaderResponse>(); |
| 117 | + const { isAuthenticated, claims } = context; |
| 118 | + |
| 119 | + return ( |
| 120 | + <div> |
| 121 | + <h1>Remix Sample</h1> |
| 122 | + {isAuthenticated ? ( |
| 123 | + <div> |
| 124 | + <p>Hello {claims?.email ?? claims?.name ?? claims?.sub}</p> |
| 125 | + <form action="/api/logto/sign-out" method="get"> |
| 126 | + <button type="submit">Sign Out</button> |
| 127 | + </form> |
| 128 | + <p> |
| 129 | + <Link to="/user-info">Example of fetching user info</Link> |
| 130 | + </p> |
| 131 | + <p> |
| 132 | + <Link to="/access-token">Example of fetching access token</Link> |
| 133 | + </p> |
| 134 | + </div> |
| 135 | + ) : ( |
| 136 | + <form action="/api/logto/sign-in" method="get"> |
| 137 | + <button type="submit">Sign In</button> |
| 138 | + </form> |
| 139 | + )} |
| 140 | + </div> |
| 141 | + ); |
| 142 | +}; |
| 143 | + |
| 144 | +export default Home; |
| 145 | +``` |
| 146 | + |
| 147 | +</Step> |
| 148 | + |
| 149 | +<Step |
| 150 | + title="Checkpoint: Test your application" |
| 151 | +> |
| 152 | + |
| 153 | +<Checkpoint /> |
| 154 | + |
| 155 | +</Step> |
| 156 | + |
| 157 | +</Steps> |
0 commit comments