From c9acc55f69e385f80d9616483ce46a2c465a925c Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Tue, 26 Oct 2021 06:00:10 -0500 Subject: [PATCH] feat: add oathkeeper/kratos testing setup and validate auth flow (#10) * chore: test changes * chore: poc logout functionality * chore: changes * chore: add oathkeeper/kratos for local testing * chore: change up some things * chore: remove code * chore: edit * chore: refactor, rename, remove unneeded docker args/env * chore: switch to updated redus-utils --- .env | 8 +- Dockerfile | 32 ------ docker-compose.yaml | 123 ++++++++++++++++++++-- docker/createJSONConfig.sh | 4 +- docker/kratos/identity.schema.json | 48 +++++++++ docker/kratos/kratos.yml | 101 ++++++++++++++++++ docker/oathkeeper/jwks.json | 0 docker/oathkeeper/kratos-selfservice.json | 69 ++++++++++++ docker/oathkeeper/oathkeeper.yml | 80 ++++++++++++++ docker/oathkeeper/rules.json | 121 +++++++++++++++++++++ package.json | 2 +- src/App/App.tsx | 9 +- src/App/connectors.ts | 5 + src/Auth/hocs.tsx | 4 +- src/Auth/sagas.ts | 26 ++++- src/Auth/selectors.ts | 1 + src/Auth/types.ts | 7 +- src/Config/build.ts | 14 +-- src/Config/slice.ts | 2 +- src/Config/types.ts | 2 +- src/bootstrap.tsx | 2 +- src/utils/api.ts | 7 +- yarn.lock | 8 +- 23 files changed, 601 insertions(+), 74 deletions(-) create mode 100644 docker/kratos/identity.schema.json create mode 100644 docker/kratos/kratos.yml create mode 100644 docker/oathkeeper/jwks.json create mode 100644 docker/oathkeeper/kratos-selfservice.json create mode 100644 docker/oathkeeper/oathkeeper.yml create mode 100644 docker/oathkeeper/rules.json diff --git a/.env b/.env index 8bb515d..b4715e2 100644 --- a/.env +++ b/.env @@ -1,9 +1,11 @@ REACT_APP_AUTH_ENABLED=true -REACT_APP_AUTH_API_BASE_URL=/api -REACT_APP_AUTH_MOCK_API=true +# Oathkeeper endpoint +REACT_APP_AUTH_API_BASE_URL=http://127.0.0.1:4455/.ory/kratos/public +REACT_APP_AUTH_MOCK_API=false REACT_APP_REMOTE_API_BASE_URL=/remote REACT_APP_REMOTE_MOCK_API=true +# Roles Microfrontend REMOTE_1_URL=http://localhost:3012 +# Transfers Microfrontend REMOTE_2_URL=http://localhost:3013 DEV_PORT=3010 -PUBLIC_PATH=http://localhost:3010/ diff --git a/Dockerfile b/Dockerfile index b7f94f7..1e6e09a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,39 +68,7 @@ RUN chmod +x /usr/share/nginx/html/createJSONConfig.sh RUN chmod +x /usr/share/nginx/html/createRemoteConfig.sh RUN chmod +x /usr/share/nginx/html/loadRuntimeConfig.sh -# Provide environment variables for setting endpoints dynamically -ARG REMOTE_API_BASE_URL -ENV REMOTE_API_BASE_URL=$REMOTE_API_BASE_URL - -ARG REMOTE_MOCK_API -ENV REMOTE_MOCK_API=$REMOTE_MOCK_API - -ARG AUTH_API_BASE_URL -ENV AUTH_API_BASE_URL=$AUTH_API_BASE_URL - -ARG AUTH_MOCK_API -ENV AUTH_MOCK_API=$AUTH_MOCK_API - -ARG AUTH_ENABLED -ENV AUTH_ENABLED=$AUTH_ENABLED - -ARG LOGIN_URL -ENV LOGIN_URL=$LOGIN_URL - -ARG LOGOUT_URL -ENV LOGOUT_URL=$LOGOUT_URL - -ARG AUTH_TOKEN_ENDPOINT -ENV AUTH_TOKEN_ENDPOINT=$AUTH_TOKEN_ENDPOINT - -ARG REMOTE_1_URL -ENV REMOTE_1_URL=$REMOTE_1_URL - -ARG REMOTE_2_URL -ENV REMOTE_2_URL=$REMOTE_2_URL - USER appuser - EXPOSE 8080 ENTRYPOINT ["/usr/share/nginx/html/entrypoint.sh"] CMD ["sh", "/usr/share/nginx/start.sh"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 522b05a..4ce85df 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,11 +1,12 @@ -version: "3.7" - +# docker-compose --profile ory --profile shell up +version: '3.7' networks: mojaloop-net: name: mojaloop-net services: reporting-hub-bop-shell: + profiles: ["shell"] container_name: reporting-hub-bop-shell image: mojaloop/reporting-hub-bop-shell build: @@ -13,14 +14,14 @@ services: cache_from: - mojaloop/reporting-hub-bop-shell environment: - # - AUTH_API_BASE_URL=/api - - AUTH_MOCK_API=false - - REMOTE_API_BASE_URL=/remote - - REMOTE_MOCK_API=true - - LOGIN_URL=http://127.0.0.1:4433/self-service/login/browser - - LOGOUT_URL=http://127.0.0.1:4433/self-service/logout + - LOGIN_URL=http://127.0.0.1:4455/.ory/kratos/public/self-service/login + - LOGOUT_URL=http://127.0.0.1:4455/.ory/kratos/public/self-service/logout/browser + - AUTH_TOKEN_URL=http://127.0.0.1:4455/.ory/kratos/public/sessions/whoami - AUTH_ENABLED=true - - AUTH_TOKEN_ENDPOINT=http://127.0.0.1:4433/sessions/whoami + - AUTH_API_BASE_URL=/ + - AUTH_MOCK_API=false + - REMOTE_API_BASE_URL=/ + - REMOTE_MOCK_API=false - REMOTE_1_URL=http://localhost:8081 - REMOTE_2_URL=http://localhost:8082 ports: @@ -32,3 +33,107 @@ services: timeout: 20s retries: 30 interval: 15s + psql: + profiles: ["ory"] + image: 'postgres:13.2' + restart: unless-stopped + environment: + POSTGRES_USER: ory-user + POSTGRES_PASSWORD: ory-pass + POSTGRES_DB: ory-data + ports: + - '5432:5432' + networks: + - mojaloop-net + kratos-migrate: + profiles: ["ory"] + depends_on: + - psql + image: 'oryd/kratos:v0.7.1-alpha.1' + restart: on-failure + command: migrate -c /etc/config/kratos/kratos.yml sql -e --yes + environment: + DSN: >- + postgres://ory-user:ory-pass@psql:5432/ory-data?sslmode=disable&max_conns=20&max_idle_conns=4 + LOG_LEVEL: debug + volumes: + - type: bind + source: ./docker/kratos + target: /etc/config/kratos + networks: + - mojaloop-net + kratos: + profiles: ["ory"] + depends_on: + - kratos-migrate + image: 'oryd/kratos:v0.7.1-alpha.1' + restart: unless-stopped + command: serve -c /etc/config/kratos/kratos.yml --dev + environment: + DSN: >- + postgres://ory-user:ory-pass@psql:5432/ory-data?sslmode=disable&max_conns=20&max_idle_conns=4 + LOG_LEVEL: trace + SERVE_PUBLIC_BASE_URL: 'http://127.0.0.1:4455/.ory/kratos/public/' + volumes: + - type: bind + source: ./docker/kratos + target: /etc/config/kratos + ports: + - '4433:4433' + - '4434:4434' + networks: + - mojaloop-net + kratos-selfservice-ui-node: + profiles: ["ory"] + depends_on: + - kratos + image: 'oryd/kratos-selfservice-ui-node:v0.7.1-alpha.1' + environment: + - 'JWKS_URL=http://oathkeeper:4456/.well-known/jwks.json' + - 'KRATOS_PUBLIC_URL=http://kratos:4433/' + - 'KRATOS_ADMIN_URL=http://kratos:4434/' + - 'KRATOS_BROWSER_URL=http://127.0.0.1:4455/.ory/kratos/public' + - PORT=4435 + - SECURITY_MODE=jwks + networks: + - mojaloop-net + mailslurper: + profiles: ["ory"] + image: 'oryd/mailslurper:latest-smtps' + ports: + - '4436:4436' + - '4437:4437' + networks: + - mojaloop-net + oathkeeper: + profiles: ["ory"] + depends_on: + - psql + image: 'oryd/oathkeeper:v0.38.14-beta.1' + restart: unless-stopped + command: serve --config=/etc/config/oathkeeper/oathkeeper.yml + environment: + LOG_LEVEL: debug + LOG_LEAK_SENSITIVE_VALUES: 'true' + TRACING_PROVIDER: jaeger + TRACING_SERVICE_NAME: Oathkeeper + TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL: 'http://jaeger:5778/sampling' + TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS: 'jaeger:6831' + TRACING_PROVIDER_JAEGER_SAMPLING_TYPE: const + TRACING_PROVIDER_JAEGER_SAMPLING_VALUE: 1 + volumes: + - type: bind + source: ./docker/oathkeeper + target: /etc/config/oathkeeper + ports: + - '4455:4455' + - '4456:4456' + networks: + - mojaloop-net + jaeger: + profiles: ["ory"] + image: jaegertracing/all-in-one:1.22 + ports: + - 16686:16686 + networks: + - mojaloop-net diff --git a/docker/createJSONConfig.sh b/docker/createJSONConfig.sh index e0059d0..ff1f6dc 100644 --- a/docker/createJSONConfig.sh +++ b/docker/createJSONConfig.sh @@ -7,9 +7,9 @@ echo "{ \"REMOTE_API_BASE_URL\": \"${REMOTE_API_BASE_URL}\", \"REMOTE_MOCK_API\": \"${REMOTE_MOCK_API}\", \"AUTH_ENABLED\": \"${AUTH_ENABLED}\", - \"AUTH_TOKEN_ENDPOINT\": \"${AUTH_TOKEN_ENDPOINT}\", \"LOGIN_URL\": \"${LOGIN_URL}\", - \"LOGOUT_URL\": \"${LOGOUT_URL}\" + \"LOGOUT_URL\": \"${LOGOUT_URL}\", + \"AUTH_TOKEN_URL\": \"${AUTH_TOKEN_URL}\" }" | jq '.' > /usr/share/nginx/html/config.json # This will exec the CMD from your Dockerfile, i.e. "npm start" diff --git a/docker/kratos/identity.schema.json b/docker/kratos/identity.schema.json new file mode 100644 index 0000000..3efc158 --- /dev/null +++ b/docker/kratos/identity.schema.json @@ -0,0 +1,48 @@ +{ + "$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" + } + } + }, + "name": { + "type": "object", + "properties": { + "first": { + "type": "string" + }, + "last": { + "type": "string" + } + } + } + }, + "required": [ + "email" + ], + "additionalProperties": false + } + } + } + \ No newline at end of file diff --git a/docker/kratos/kratos.yml b/docker/kratos/kratos.yml new file mode 100644 index 0000000..a85b849 --- /dev/null +++ b/docker/kratos/kratos.yml @@ -0,0 +1,101 @@ +courier: + smtp: + connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true + +hashers: + algorithm: bcrypt + bcrypt: + cost: 12 + +identity: + default_schema_url: file:///etc/config/kratos/identity.schema.json + +log: + level: debug + format: text + leak_sensitive_values: true + +secrets: + cookie: + - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE + +selfservice: + default_browser_return_url: http://127.0.0.1:4455/ + # Important to configure! + whitelisted_return_urls: + - http://127.0.0.1:4455 + - http://127.0.0.1:3010 + - http://localhost:3010 + - http://127.0.0.1:8080 + - http://localhost:8080 + methods: + password: + enabled: true + flows: + error: + ui_url: http://127.0.0.1:4455/error + settings: + ui_url: http://127.0.0.1:4455/settings + privileged_session_max_age: 15m + recovery: + enabled: true + ui_url: http://127.0.0.1:4455/recovery + verification: + enabled: true + ui_url: http://127.0.0.1:4455/verify + after: + default_browser_return_url: http://127.0.0.1:4455/ + logout: + after: + default_browser_return_url: http://127.0.0.1:4455/auth/login + login: + ui_url: http://127.0.0.1:4455/auth/login + after: + # Important to configure! + default_browser_return_url: http://127.0.0.1:8080 + lifespan: 10m + registration: + lifespan: 10m + ui_url: http://127.0.0.1:4455/auth/registration + after: + password: + hooks: + - hook: session + +serve: + public: + base_url: http://127.0.0.1:4433/ + cors: + # Important to configure! + allowed_origins: + - http://127.0.0.1:3010 + - http://localhost:3010 + - http://127.0.0.1:8080 + - http://localhost:8080 + - http://127.0.0.1:4455 + allowed_methods: + - POST + - GET + - PUT + - PATCH + - DELETE + allowed_headers: + - Authorization + - Cookie + - Content-Type + - X-Session-Token + exposed_headers: + - Content-Type + - Set-Cookie + allow_credentials: true + options_passthrough: false + debug: true + enabled: true + host: "0.0.0.0" + port: 4433 + admin: + base_url: http://kratos:4434/ + host: "0.0.0.0" + port: 4434 + +sqa-opt-out: false diff --git a/docker/oathkeeper/jwks.json b/docker/oathkeeper/jwks.json new file mode 100644 index 0000000..e69de29 diff --git a/docker/oathkeeper/kratos-selfservice.json b/docker/oathkeeper/kratos-selfservice.json new file mode 100644 index 0000000..dc2c70d --- /dev/null +++ b/docker/oathkeeper/kratos-selfservice.json @@ -0,0 +1,69 @@ +[ + { + "id": "ory:kratos:public", + "upstream": { + "url": "http://kratos:4433", + "preserve_host": true, + "strip_path": "/.ory/kratos/public" + }, + "match": { + "url": "http://127.0.0.1:4455/.ory/kratos/public/<**>", + "methods": ["GET", "POST" ,"PUT", "DELETE", "PATCH"] + }, + "authenticators": [{ + "handler": "noop" + }], + "authorizer": { + "handler": "allow" + }, + "mutators": [{ + "handler": "noop" + }] + }, + { + "id": "ory:kratos-selfservice-ui-node:anonymous", + "upstream": { + "preserve_host": true, + "url": "http://kratos-selfservice-ui-node:4435" + }, + "match": { + "url": "http://127.0.0.1:4455/<{error,recovery,verify,auth/*,**.css,**.js}{/,}>", + "methods": ["GET"] + }, + "authenticators": [{ + "handler": "anonymous" + }], + "authorizer": { + "handler": "allow" + }, + "mutators": [{ + "handler": "noop" + }] + }, + { + "id": "ory:kratos-selfservice-ui-node:protected", + "upstream": { + "preserve_host": true, + "url": "http://kratos-selfservice-ui-node:4435" + }, + "match": { + "url": "http://127.0.0.1:4455/<{,debug,dashboard,settings}>", + "methods": ["GET"] + }, + "authenticators": [{ + "handler": "cookie_session" + }], + "authorizer": { + "handler": "allow" + }, + "mutators": [{ + "handler": "id_token" + }], + "errors": [{ + "handler": "redirect", + "config": { + "to": "http://127.0.0.1:4455/auth/login" + } + }] + } +] diff --git a/docker/oathkeeper/oathkeeper.yml b/docker/oathkeeper/oathkeeper.yml new file mode 100644 index 0000000..8681af6 --- /dev/null +++ b/docker/oathkeeper/oathkeeper.yml @@ -0,0 +1,80 @@ +serve: + proxy: + port: 4455 # run the proxy at port 4455 + api: + port: 4456 # run the api at port 4456 + +access_rules: + matching_strategy: glob + repositories: + - file:///etc/config/oathkeeper/rules.json + - file:///etc/config/oathkeeper/kratos-selfservice.json + +errors: + fallback: + - json + handlers: + json: + enabled: true + config: + verbose: true + redirect: + enabled: true + config: + to: https://www.ory.sh/docs + +mutators: + header: + enabled: true + config: + headers: + X-User: "{{ print .Subject }}" + noop: + enabled: true + id_token: + enabled: true + config: + issuer_url: http://localhost:4455/ + jwks_url: file:///etc/config/oathkeeper/jwks.json + +authorizers: + allow: + enabled: true + deny: + enabled: true + remote_json: + # remote_json documentation: https://www.ory.sh/oathkeeper/docs/pipeline/authz#remote_json + enabled: true + config: + remote: http://keto:4466/check + # must be set explicitly, otherwise: https://github.com/ory/oathkeeper/issues/797 + forward_response_headers_to_upstream: [] + payload: | + { + "namespace": "...", + "subject": "...", + "object": "...", + "relation": "..." + } + +authenticators: + cookie_session: + enabled: true + config: + check_session_url: http://kratos:4433/sessions/whoami + preserve_path: true + extra_from: "@this" + subject_from: "identity.id" + only: + - ory_kratos_session + noop: + enabled: true + anonymous: + enabled: true + config: + subject: guest + oauth2_introspection: + enabled: true + config: + introspection_url: http://hydra:4445/oauth2/introspect + \ No newline at end of file diff --git a/docker/oathkeeper/rules.json b/docker/oathkeeper/rules.json new file mode 100644 index 0000000..86ec478 --- /dev/null +++ b/docker/oathkeeper/rules.json @@ -0,0 +1,121 @@ +[ + { + "id": "allow-anonymous-with-header-mutator", + "upstream": { + "url": "https://httpbin.org/anything/header" + }, + "match": { + "url": "http://127.0.0.1:4455/anything/header", + "methods": [ "GET" ] + }, + "authenticators": [{ + "handler": "anonymous" + }], + "authorizer": { + "handler": "allow" + }, + "mutators": [{ + "handler": "header", + "config": { + "headers": { + "X-User": "{{ print .Subject }}" + } + } + }] + }, + { + "id": "deny-anonymous", + "upstream": { + "url": "https://httpbin.org/anything/deny" + }, + "match": { + "url": "http://127.0.0.1:4455/anything/deny", + "methods": [ "GET" ] + }, + "authenticators": [{ + "handler": "anonymous" + }], + "authorizer": { + "handler": "deny" + }, + "mutators": [{ + "handler": "noop" + }], + "errors": [ + { + "handler": "json", + "config": { + "when": [{ + "request": { + "header": { + "accept": ["application/json"] + } + } + }] + } + }, + { + "handler": "redirect", + "config": { + "when": [{ + "request": { + "header": { + "accept": ["text/*"] + } + } + }] + } + } + ] + }, + { + "id": "allow-anonymous-with-id-token-mutator", + "upstream": { + "url": "https://httpbin.org/anything/id_token" + }, + "match": { + "url": "http://127.0.0.1:4455/anything/id_token", + "methods": [ "GET" ] + }, + "authenticators": [{ + "handler": "anonymous" + }], + "authorizer": { + "handler": "allow" + }, + "mutators": [{ + "handler": "id_token" + }] + }, + + { + "id": "allow-with-token-and-keto", + "upstream": { + "url": "https://httpbin.org/anything/token-and-keto" + }, + "match": { + "url": "http://127.0.0.1:4455/anything/token-and-keto", + "methods": [ "GET" ] + }, + "authenticators": [{ + "handler": "oauth2_introspection", + "config": { + "scope_strategy": "exact", + "required_scope": ["openid", "offline"], + "cache": { + "enabled": false + } + } + }], + "authorizer": { + "handler": "remote_json", + "config": { + "remote": "http://keto:4466/check", + "payload": "{\"namespace\": \"default-namespace\", \"subject\": \"{{ print .Subject }}\", \"object\": \"token:and:keto\", \"relation\": \"get\"}" + } + }, + "mutators": [{ + "handler": "noop" + }] + } +] diff --git a/package.json b/package.json index 52672e8..980fc45 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@babel/preset-typescript": "^7.10.4", "@modusbox/microfrontend-utils": "^0.0.3", "@modusbox/react-components": "^0.1.4", - "@modusbox/redux-utils": "^0.0.10", + "@modusbox/redux-utils": "^0.0.11", "@modusbox/ts-utils": "^0.0.5", "@reduxjs/toolkit": "^1.4.0", "@svgr/webpack": "^5.5.0", diff --git a/src/App/App.tsx b/src/App/App.tsx index 1fb99a0..9cae2a1 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -5,7 +5,7 @@ import appConnector, { AppProps } from './connectors'; import { Remote } from './types'; import './App.scss'; -function App({ onMount, remotes }: AppProps) { +function App({ userEmail, onMount, remotes, logout }: AppProps) { useEffect(() => { onMount(); }, []); @@ -21,7 +21,12 @@ function App({ onMount, remotes }: AppProps) { return ( - + {/* TODO: Preferably we pop up a menu here */} +
{content} diff --git a/src/App/connectors.ts b/src/App/connectors.ts index edcff7b..5696492 100644 --- a/src/App/connectors.ts +++ b/src/App/connectors.ts @@ -3,13 +3,18 @@ import ReduxContext from 'store/context'; import { connect, ConnectedProps } from 'react-redux'; import { actions } from './slice'; import * as selectors from './selectors'; +import * as authSelectors from '../Auth/selectors'; +import { actions as authActions } from '../Auth/slice'; const appConnector = connect( (state: State) => ({ remotes: selectors.getRemotes(state), + isLoggedIn: authSelectors.getIsLoggedIn(state), + userEmail: authSelectors.getUserEmail(state), }), (dispatch: Dispatch) => ({ onMount: () => dispatch(actions.requestRemotes()), + logout: () => dispatch(authActions.logout()), }), null, { context: ReduxContext }, diff --git a/src/Auth/hocs.tsx b/src/Auth/hocs.tsx index a604668..332104a 100644 --- a/src/Auth/hocs.tsx +++ b/src/Auth/hocs.tsx @@ -6,14 +6,12 @@ import Auth from './Auth'; export function withAuth(App: ComponentType) { const AuthLoader = (props: AuthProps): JSX.Element => { const { isAuthEnabled, isLoggedIn, userEmail, doAuth, onLogoutClick } = props; - useEffect(() => { if (isAuthEnabled) { doAuth(); } }, []); - - if (!isAuthEnabled || isLoggedIn) { + if (isAuthEnabled && isLoggedIn) { const authAppProps: AuthAppProps = { onLogoutClick, userEmail, diff --git a/src/Auth/sagas.ts b/src/Auth/sagas.ts index aeca8f7..7ad6e36 100644 --- a/src/Auth/sagas.ts +++ b/src/Auth/sagas.ts @@ -1,12 +1,17 @@ -import apis from 'utils/api'; import { is200, is401 } from '@modusbox/ts-utils/lib/http'; import { all, call, put, select, takeLatest } from 'redux-saga/effects'; +import axios from 'axios'; +import api from 'utils/api'; +import { MakeResponse } from '@modusbox/redux-utils/lib/api'; import * as selectors from './selectors'; import { actions } from './slice'; +import { LoggedUser } from './types'; function* doAuth() { try { - const { status, data } = yield call(apis.whoami.read, {}); + // @ts-ignore + const response = yield call(api.whoami.read) as MakeResponse; + const { data, status } = response; if (is200(status)) { yield put(actions.doAuthSuccess(data)); @@ -27,7 +32,22 @@ function* doAuth() { } function* logout() { - window.location.href = yield select(selectors.getLogoutEndpoint); + const logoutEndpoint: string = yield select(selectors.getLogoutEndpoint); + const apiCall = () => { + return axios + .get(logoutEndpoint, { + withCredentials: true, + }) + .then((response) => { + return response; + }) + .catch((err) => { + return err.response; + }); + }; + + const { data } = yield call(apiCall); + window.location.href = data.logout_url; } function* doAuthSaga() { diff --git a/src/Auth/selectors.ts b/src/Auth/selectors.ts index 890af81..7aeaa1c 100644 --- a/src/Auth/selectors.ts +++ b/src/Auth/selectors.ts @@ -4,6 +4,7 @@ import { State } from 'store'; export const getIsAuthEnabled = (state: State) => state.config.auth.isAuthEnabled; export const getLoginEndpoint = (state: State) => state.config.auth.loginEndpoint; export const getLogoutEndpoint = (state: State) => state.config.auth.logoutEndpoint; +export const getAuthTokenEndpoint = (state: State) => state.config.auth.authTokenEndpoint; export const getIsAuthPending = (state: State) => state.auth.isAuthPending; export const getIsLoggedIn = (state: State) => state.auth.isLoggedIn; diff --git a/src/Auth/types.ts b/src/Auth/types.ts index 91c8e0a..6c177df 100644 --- a/src/Auth/types.ts +++ b/src/Auth/types.ts @@ -8,8 +8,13 @@ export interface LoggedUser { id: string; schema_id: string; schema_url: string; + state: string; + state_changed_at: string; traits: { - name: string; + name: { + last: string; + first: string; + }; role: string; email: string; }; diff --git a/src/Config/build.ts b/src/Config/build.ts index e915134..4ec3ac3 100644 --- a/src/Config/build.ts +++ b/src/Config/build.ts @@ -5,9 +5,9 @@ export default async (): Promise => { const baseUrl = `${protocol}//${host}`; // Using the same protocol as we've been loaded from to avoid Mixed Content error. const defaults = { - loginEndpoint: `${baseUrl}/auth/auth/login`, - logoutEndpoint: `${baseUrl}/kratos/self-service/browser/flows/logout`, - tokenEndpoint: `${baseUrl}/kratos/sessions/whoami`, + loginEndpoint: `${process.env.REACT_APP_AUTH_API_BASE_URL}/self-service/login`, + logoutEndpoint: `${process.env.REACT_APP_AUTH_API_BASE_URL}/self-service/logout/browser`, + authTokenEndpoint: `${process.env.REACT_APP_AUTH_API_BASE_URL}/sessions/whoami`, isAuthEnabled: process.env.REACT_APP_AUTH_ENABLED !== 'false', basename: baseUrl, authApiBaseUrl: `${process.env.REACT_APP_AUTH_API_BASE_URL}`, @@ -25,9 +25,9 @@ export default async (): Promise => { REMOTE_API_BASE_URL, REMOTE_MOCK_API, AUTH_ENABLED, - AUTH_TOKEN_ENDPOINT, LOGIN_URL, LOGOUT_URL, + AUTH_TOKEN_URL, } = await fetch(`${baseUrl}/config.json`).then((response) => response.json()); if (LOGIN_URL !== undefined) { @@ -36,6 +36,9 @@ export default async (): Promise => { if (LOGOUT_URL !== undefined) { config.logoutEndpoint = LOGOUT_URL; } + if (AUTH_TOKEN_URL !== undefined) { + config.authTokenEndpoint = AUTH_TOKEN_URL; + } if (AUTH_API_BASE_URL !== undefined) { config.authApiBaseUrl = AUTH_API_BASE_URL; } @@ -53,9 +56,6 @@ export default async (): Promise => { if (AUTH_ENABLED !== undefined) { config.isAuthEnabled = AUTH_ENABLED !== 'false'; } - if (AUTH_TOKEN_ENDPOINT !== undefined) { - config.tokenEndpoint = AUTH_TOKEN_ENDPOINT; - } } catch (err) { // eslint-disable-next-line console.info('config returned error', err); diff --git a/src/Config/slice.ts b/src/Config/slice.ts index 3364da8..b5ad8b0 100644 --- a/src/Config/slice.ts +++ b/src/Config/slice.ts @@ -12,7 +12,7 @@ export const initialState: ConfigState = { remoteMockApi: false, }, auth: { - tokenEndpoint: '', + authTokenEndpoint: '', loginEndpoint: '', logoutEndpoint: '', isAuthEnabled: true, diff --git a/src/Config/types.ts b/src/Config/types.ts index 5dabd05..a4f55f5 100644 --- a/src/Config/types.ts +++ b/src/Config/types.ts @@ -12,7 +12,7 @@ export type ApiConfig = { export interface AuthConfig { loginEndpoint: string; logoutEndpoint: string; - tokenEndpoint: string; + authTokenEndpoint: string; isAuthEnabled: boolean; } diff --git a/src/bootstrap.tsx b/src/bootstrap.tsx index 9adc306..c7b5161 100644 --- a/src/bootstrap.tsx +++ b/src/bootstrap.tsx @@ -30,7 +30,7 @@ async function boot() { auth: { loginEndpoint: config.loginEndpoint, logoutEndpoint: config.logoutEndpoint, - tokenEndpoint: config.tokenEndpoint, + authTokenEndpoint: config.authTokenEndpoint, isAuthEnabled: config.isAuthEnabled, }, }, diff --git a/src/utils/api.ts b/src/utils/api.ts index c9cba05..5074628 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,11 +1,10 @@ import { State } from 'store'; -import authMock from 'Auth/_mockData'; import remotesMock from 'App/_mockData'; import buildApis, { buildEndpointBuilder } from '@modusbox/redux-utils/lib/api'; const services = { kratos: { - baseUrl: (state: State) => state.config.api.authApiBaseUrl, + baseUrl: () => '', mock: (state: State) => state.config.api.authMockApi, }, mainApi: { @@ -19,8 +18,8 @@ const builder = buildEndpointBuilder(); export default buildApis({ whoami: builder({ service: services.kratos, - url: (state: State) => state.config.auth.tokenEndpoint, - mock: authMock, + url: (state: State) => state.config.auth.authTokenEndpoint, + withCredentials: true, }), // NOTE: instead of an actual api, using local json file to store // remote microfrontend config diff --git a/yarn.lock b/yarn.lock index 354ada2..65907d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1255,10 +1255,10 @@ date-fns "^2.21.3" lodash "^4.17.21" -"@modusbox/redux-utils@^0.0.10": - version "0.0.10" - resolved "https://registry.yarnpkg.com/@modusbox/redux-utils/-/redux-utils-0.0.10.tgz#31afc83bb38caf87341ab697da02dee9282dabfd" - integrity sha512-3dY+gbj+j2gywCbNG6aUN+Grs6yF4RIv2qrO7tHMZCfF0v/3N4eXIBmuohZUoIFR5q9A4yvx+xT0XwbQpKPwRA== +"@modusbox/redux-utils@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@modusbox/redux-utils/-/redux-utils-0.0.11.tgz#a0504a8075ae68b724aa221536c782214140a55a" + integrity sha512-8jYWavoYiXl3dpQlDgixQQRmTZphH/sWZsjZRXVH2LfgJfR1kJVuQZv1HiZ7XwrzEknH3ZqXIWxQk6+rMvE83w== dependencies: "@modusbox/ts-utils" "^0.0.3" axios "^0.21.1"