Skip to content

Commit

Permalink
Allow Kratos provider redirect (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
kalinkrustev authored Feb 14, 2024
1 parent 4cba785 commit 45dfc0b
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ executors:

default-machine:
machine:
image: ubuntu-1604:201903-01
image: ubuntu-2004:202201-02

##
# Jobs
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:lts-alpine as builder
FROM node:14.18.1-alpine as builder
WORKDIR /opt/reporting-hub-bop-shell
ENV PATH /opt/reporting-hub-bop-shell/node_modules/.bin:$PATH

Expand Down
1 change: 1 addition & 0 deletions docker/createJSONConfig.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ echo "{
\"REMOTE_MOCK_API\": \"${REMOTE_MOCK_API}\",
\"AUTH_ENABLED\": \"${AUTH_ENABLED}\",
\"LOGIN_URL\": \"${LOGIN_URL}\",
\"LOGIN_PROVIDER\": \"${LOGIN_PROVIDER}\",
\"LOGOUT_URL\": \"${LOGOUT_URL}\",
\"AUTH_TOKEN_URL\": \"${AUTH_TOKEN_URL}\"
}" | jq '.' > /usr/share/nginx/html/config.json
Expand Down
1 change: 1 addition & 0 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The application is driven by some environment variables used at build and runtim
|---|---|
| `API_BASE_URL` | base path for api (could be a URL) |
| `LOGIN_URL` | where to redirect for login |
| `LOGIN_PROVIDER` | enable automatic redirect to Kratos provider |
| `LOGOUT_URL` | where to redirect for logout |
| `MOCK_API` | enable mocking api |
| `AUTH_ENABLED` | enable auth |
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mojaloop/reporting-hub-bop-shell",
"version": "2.1.1",
"version": "2.2.0",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
62 changes: 58 additions & 4 deletions src/Auth/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,76 @@ import { LoggedUser } from './types';

function* doAuth() {
try {
// @ts-ignore
const response = yield call(api.whoami.read) as MakeResponse<LoggedUser>;
let response: MakeResponse<LoggedUser>;
try {
response = yield call(api.whoami.read);
} catch (error) {
response = error.response;
if (!response) throw error;
}
const { data, status } = response;

if (is200(status)) {
yield put(actions.doAuthSuccess(data));
} else if (is401(status)) {
window.location.href = yield select(selectors.getLoginEndpoint);
const loginProvider: string = yield select(selectors.getLoginProvider);
const loginEndpoint: string = yield select(selectors.getLoginEndpoint);
const loginUrl: string = `${loginEndpoint}?return_to=${window.location.href}`;
if (loginProvider) {
const loginResult: string = yield call(async () => {
try {
// obtain login flow
const {
ui: { method, action, nodes },
} = await (await fetch(loginUrl, { headers: { accept: 'application/json' } })).json();
const form = document.createElement('form');
form.method = method;
form.action = action;
let submit: HTMLInputElement | undefined;

nodes.forEach(
({
attributes: { type, name, node_type, value },
}: {
attributes: Record<string, string>;
}) => {
if (name === 'provider' && value !== loginProvider) return;
const element = document.createElement(node_type) as HTMLInputElement;
if (name === 'provider') submit = element;
element.type = type;
element.value = value;
element.name = name;
form.appendChild(element);
},
);

if (submit) {
document.body.appendChild(form);
submit.click();
} else {
window.location.assign(loginUrl);
}
} catch (error) {
console.error(error);
return 'Currently unable to perform authentication. Please try again later';
}
return '';
});
if (loginResult) {
yield put(actions.doAuthFailed(loginResult));
}
} else {
window.location.href = loginUrl;
}
} else {
yield put(
actions.doAuthFailed(
'There was an error while performing authentication. Please try again later',
),
);
}
} catch (e) {
} catch (error) {
console.error(error);
yield put(
actions.doAuthFailed('Currently unable to perform authentication. Please try again later'),
);
Expand Down
1 change: 1 addition & 0 deletions src/Auth/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,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 getLoginProvider = (state: State) => state.config.auth.loginProvider;
export const getLogoutEndpoint = (state: State) => state.config.auth.logoutEndpoint;
export const getAuthTokenEndpoint = (state: State) => state.config.auth.authTokenEndpoint;

Expand Down
5 changes: 5 additions & 0 deletions src/Config/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default async (): Promise<AppConfig & AuthConfig & ApiConfig> => {
// Using the same protocol as we've been loaded from to avoid Mixed Content error.
const defaults = {
loginEndpoint: `${process.env.REACT_APP_AUTH_API_BASE_URL}/self-service/login`,
loginProvider: '',
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',
Expand All @@ -26,13 +27,17 @@ export default async (): Promise<AppConfig & AuthConfig & ApiConfig> => {
REMOTE_MOCK_API,
AUTH_ENABLED,
LOGIN_URL,
LOGIN_PROVIDER,
LOGOUT_URL,
AUTH_TOKEN_URL,
} = await fetch(`${baseUrl}/config.json`).then((response) => response.json());

if (LOGIN_URL !== undefined) {
config.loginEndpoint = LOGIN_URL;
}
if (LOGIN_PROVIDER !== undefined) {
config.loginProvider = LOGIN_PROVIDER;
}
if (LOGOUT_URL !== undefined) {
config.logoutEndpoint = LOGOUT_URL;
}
Expand Down
1 change: 1 addition & 0 deletions src/Config/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const initialState: ConfigState = {
auth: {
authTokenEndpoint: '',
loginEndpoint: '',
loginProvider: '',
logoutEndpoint: '',
isAuthEnabled: true,
},
Expand Down
1 change: 1 addition & 0 deletions src/Config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type ApiConfig = {

export interface AuthConfig {
loginEndpoint: string;
loginProvider: string;
logoutEndpoint: string;
authTokenEndpoint: string;
isAuthEnabled: boolean;
Expand Down

0 comments on commit 45dfc0b

Please sign in to comment.