diff --git a/app/pages/DeviceAuthSuccessPage.tsx b/app/pages/DeviceAuthSuccessPage.tsx new file mode 100644 index 000000000..dd90a992c --- /dev/null +++ b/app/pages/DeviceAuthSuccessPage.tsx @@ -0,0 +1,17 @@ +import { Success16Icon } from '@oxide/ui' + +/** + * Device authorization success page + */ +export default function DeviceAuthSuccessPage() { + return ( +
+

Device authentication

+

+ + Success! +

+

Your device is now logged in.

+
+ ) +} diff --git a/app/pages/DeviceAuthVerifyPage.tsx b/app/pages/DeviceAuthVerifyPage.tsx new file mode 100644 index 000000000..5d01f17b1 --- /dev/null +++ b/app/pages/DeviceAuthVerifyPage.tsx @@ -0,0 +1,48 @@ +import { useNavigate, useSearchParams } from 'react-router-dom' + +import { useApiMutation } from '@oxide/api' +import { Button, Warning12Icon } from '@oxide/ui' + +import { useToast } from '../hooks' + +/** + * Device authorization verification page + */ +export default function DeviceAuthVerifyPage() { + const [searchParams] = useSearchParams() + const navigate = useNavigate() + const addToast = useToast() + const confirmPost = useApiMutation('deviceAuthConfirm', { + onSuccess: () => { + navigate('/device/success') + }, + onError: () => { + addToast({ + title: 'Token denied', + icon: , + variant: 'error', + timeout: 4000, + }) + }, + }) + const userCode = searchParams.get('user_code') + + return ( +
+

Device authentication

+

Make sure this code matches the one shown on the device you are authenticating.

+

{userCode}

+ +
+ ) +} diff --git a/app/routes.tsx b/app/routes.tsx index 733bfda42..fff239bee 100644 --- a/app/routes.tsx +++ b/app/routes.tsx @@ -9,6 +9,8 @@ import OrgLayout from './layouts/OrgLayout' import ProjectLayout from './layouts/ProjectLayout' import RootLayout from './layouts/RootLayout' import SettingsLayout from './layouts/SettingsLayout' +import DeviceAuthSuccessPage from './pages/DeviceAuthSuccessPage' +import DeviceAuthVerifyPage from './pages/DeviceAuthVerifyPage' import LoginPage from './pages/LoginPage' import NotFound from './pages/NotFound' import { OrgAccessPage } from './pages/OrgAccessPage' @@ -44,6 +46,11 @@ export const Router = () => ( } /> + }> + } /> + } /> + + } /> }> diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index d2d2072bf..0243b532d 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -875,4 +875,14 @@ export const handlers = [ rest.get | GetErr>('/api/users', (req, res) => { return res(json(paginated(req.url.search, db.users))) }), + + rest.post, never, PostErr>( + '/api/device/confirm', + (req, res, ctx) => { + if (req.body.user_code === 'BADD-CODE') { + return res(ctx.status(404)) + } + return res(ctx.status(200)) + } + ), ]