Skip to content
This repository has been archived by the owner on Feb 2, 2024. It is now read-only.

Commit

Permalink
feat: add kratos boilerplate behind feature flag (#54)
Browse files Browse the repository at this point in the history
* feat: add boilerplate for auth routes

* fix: src/components/root-component.tsx

Co-authored-by: Samer Buna <[email protected]>

Co-authored-by: Samer Buna <[email protected]>
  • Loading branch information
bodymindarts and samerbuna authored Feb 11, 2022
1 parent c9df056 commit f26e3b6
Show file tree
Hide file tree
Showing 20 changed files with 432 additions and 43 deletions.
5 changes: 4 additions & 1 deletion .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@

"SESSION_KEYS": "sessionkeys",

"AUTH_ENDPOINT": "http://localhost:3000/api/login"
"AUTH_ENDPOINT": "http://localhost:3000/api/login",
"AUTH_BASE_URL": "http://localhost:3000/api",
"KRATOS_API_BASE_URL": "http://localhost:4433/",
"KRATOS_BROWSER_URL": "http://localhost:4433/"
}
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module.exports = {
"init-declarations": "error",
"jsx-quotes": "error",
"linebreak-style": ["error", "unix"],
"max-depth": ["error", { max: 3 }],
"max-depth": ["error", { max: 4 }],
"max-lines-per-function": ["error", { max: 500 }],
"max-lines": ["error", { max: 500 }],
"max-nested-callbacks": ["error", { max: 3 }],
Expand Down
34 changes: 34 additions & 0 deletions dev/email-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
},
"verification": {
"via": "email"
},
"recovery": {
"via": "email"
}
}
}
},
"required": ["email"],
"additionalProperties": false
}
}
}
80 changes: 80 additions & 0 deletions dev/kratos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
version: v0.8.2-alpha.1

dsn: memory

serve:
public:
base_url: http://localhost:4433/
cors:
enabled: true
admin:
base_url: http://kratos:4434/

selfservice:
default_browser_return_url: http://localhost:3000/
whitelisted_return_urls:
- http://localhost:3000

methods:
password:
enabled: true

flows:
error:
ui_url: http://localhost:4455/error

settings:
ui_url: http://localhost:4455/settings
privileged_session_max_age: 15m

recovery:
enabled: true
ui_url: http://localhost:4455/recovery

verification:
enabled: true
ui_url: http://localhost:4455/verification
after:
default_browser_return_url: http://localhost:4455/

logout:
after:
default_browser_return_url: http://localhost:4455/login

login:
ui_url: http://localhost:4455/login
lifespan: 10m

registration:
lifespan: 10m
ui_url: http://localhost:3000/register/email
after:
password:
hooks:
- hook: session

log:
level: debug
format: text
leak_sensitive_values: true

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL

ciphers:
algorithm: xchacha20-poly1305

hashers:
algorithm: bcrypt
bcrypt:
cost: 8

identity:
default_schema_url: file:///etc/config/kratos/identity.schema.json

courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true
62 changes: 62 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
version: "3.7"

services:
kratos-migrate:
image: oryd/kratos:v0.8.2-alpha.1
depends_on:
- postgres
environment:
- DSN=postgres://kratos:secret@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes
restart: on-failure
volumes:
- type: bind
source: ./dev/kratos.yml
target: /etc/config/kratos/kratos.yml
- type: bind
source: ./dev/email-schema.json
target: /etc/config/kratos/identity.schema.json
networks:
- intranet

kratos:
image: oryd/kratos:v0.8.2-alpha.1
command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier
depends_on:
- postgres
volumes:
- type: bind
source: ./dev/kratos.yml
target: /etc/config/kratos/kratos.yml
- type: bind
source: ./dev/email-schema.json
target: /etc/config/kratos/identity.schema.json
environment:
- DSN=postgres://kratos:secret@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
ports:
- "4433:4433" # public
- "4434:4434" # admin
networks:
- intranet

postgres:
image: postgres:9.6
ports:
- "5432:5432"
environment:
- POSTGRES_USER=kratos
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=kratos
networks:
- intranet

mailslurper:
image: oryd/mailslurper:latest-smtps
ports:
- "4436:4436"
- "4437:4437"
networks:
- intranet

networks:
intranet:
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"dependencies": {
"@galoymoney/client": "^0.0.21",
"@galoymoney/react": "^0.0.15",
"@ory/client": "^0.0.1-alpha.49",
"@ory/kratos-client": "^0.8.2-alpha.1",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
Expand Down
16 changes: 16 additions & 0 deletions src/components/pages/register.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SelfServiceRegistrationFlow } from "@ory/kratos-client"
import { useState } from "react"

interface RegisterProps {
flowData?: KratosFlowData
}

const Register = ({ flowData: flowDataProp }: RegisterProps) => {
const [flowData] = useState<SelfServiceRegistrationFlow | undefined>(
flowDataProp?.registrationData,
)

return <div className="register">{JSON.stringify(flowData)}</div>
}

export default Register
66 changes: 45 additions & 21 deletions src/components/root-component.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,64 @@
import { kratosFeatureFlag } from "../kratos"
import { Suspense } from "react"
import { ErrorBoundary } from "react-error-boundary"

import { Spinner } from "@galoymoney/react"

import appRoutes, { checkRoute } from "../server/routes"
import { appRoutes, checkRoute, authRoutes, checkAuthRoute } from "../server/routes"

import ErrorFallback from "./error-fallback"

type Props = {
path: RoutePath
flowData?: KratosFlowData
[name: string]: unknown
}

const RootComponent = ({ path, ...props }: Props) => {
const RootComponent = ({ path, flowData, ...props }: Props) => {
const checkedRoutePath = checkRoute(path)
if (checkedRoutePath instanceof Error) {
throw checkedRoutePath
if (!(checkedRoutePath instanceof Error)) {
const Component = appRoutes[checkedRoutePath].component

return (
<Suspense
fallback={
<div className="suspense-fallback">
<Spinner size="big" />
</div>
}
>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<div id="main-container">
<Component {...props} />
</div>
</ErrorBoundary>
</Suspense>
)
}
if (kratosFeatureFlag) {
const checkedAuthRoutePath = checkAuthRoute(path)
if (!(checkedAuthRoutePath instanceof Error)) {
const Component = authRoutes[checkedAuthRoutePath].component

return (
<Suspense
fallback={
<div className="suspense-fallback">
<Spinner size="big" />
</div>
}
>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<div id="main-container">
<Component flowData={flowData} {...props} />
</div>
</ErrorBoundary>
</Suspense>
)
}
}

const Component = appRoutes[checkedRoutePath].component

return (
<Suspense
fallback={
<div className="suspense-fallback">
<Spinner size="big" />
</div>
}
>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<div id="main-container">
<Component {...props} />
</div>
</ErrorBoundary>
</Suspense>
)
throw checkedRoutePath
}

export default RootComponent
10 changes: 8 additions & 2 deletions src/components/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ const Root = ({ GwwState }: RootProps) => {
return (
<AuthProvider>
<GwwContext.Provider value={{ state, dispatch }}>
<RootComponent path={state.path} key={state.key} {...state.props} />
<RootComponent
path={state.path}
key={state.key}
flowData={state.flowData}
{...state.props}
/>
</GwwContext.Provider>
</AuthProvider>
)
Expand All @@ -43,6 +48,7 @@ type SSRootProps = {
client: GaloyClient<unknown>
galoyJwtToken?: string
GwwState: GwwState
flowData?: KratosFlowData
}

export const SSRRoot = ({ client, GwwState, galoyJwtToken }: SSRootProps) => {
Expand All @@ -54,7 +60,7 @@ export const SSRRoot = ({ client, GwwState, galoyJwtToken }: SSRootProps) => {
return (
<AuthProvider galoyClient={client} galoyJwtToken={galoyJwtToken}>
<GwwContext.Provider value={{ state, dispatch }}>
<RootComponent path={state.path} {...state.props} />
<RootComponent path={state.path} flowData={state.flowData} {...state.props} />
</GwwContext.Provider>
</AuthProvider>
)
Expand Down
12 changes: 12 additions & 0 deletions src/kratos/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Configuration } from "@ory/client"
import { V0alpha2ApiInterface, V0alpha2Api } from "@ory/kratos-client"

const kratosApiBaseUrlInternal = process.env.KRATOS_API_BASE_URL as string

export const kratosBrowserUrl = process.env.KRATOS_BROWSER_URL as string

export const kratosFeatureFlag = Boolean(process.env.KRATOS_FEATURE_FLAG || false)

export const kratosSdk: V0alpha2ApiInterface = new V0alpha2Api(
new Configuration({ basePath: kratosApiBaseUrlInternal }),
) as unknown as V0alpha2ApiInterface
42 changes: 42 additions & 0 deletions src/kratos/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable camelcase */

import { SelfServiceRegistrationFlow } from "@ory/kratos-client"
import { Request } from "express"
import { getUrlForFlow, isQuerySet } from "./helpers"
import { kratosSdk } from "./config"

export const handleRegister = async (req: Request): Promise<HandleRegisterResponse> => {
const { flow, return_to = "" } = req.query

const initFlowUrl = getUrlForFlow(
"registration",
new URLSearchParams({ return_to: return_to.toString() }),
)

// The flow is used to identify the settings and registration flow and
// return data like the csrf_token and so on.
if (!isQuerySet(flow)) {
return { redirect: true, redirectTo: initFlowUrl }
}

try {
const { data }: { data: SelfServiceRegistrationFlow } =
await kratosSdk.getSelfServiceRegistrationFlow(flow, req.header("Cookie"))
return { redirect: false, flowData: { registrationData: data } }
} catch (error: any) {
switch (error?.response?.status) {
// Flow ID is old
case 410:
case 404: {
return { redirect: true, redirectTo: initFlowUrl }
}
default: {
console.log("Error while fetching registration flow", {
error,
flow,
})
throw error
}
}
}
}
Loading

0 comments on commit f26e3b6

Please sign in to comment.