Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/integration_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: cachix/install-nix-action@v13
- uses: cachix/install-nix-action@v22
with:
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz
- name: Install dependencies in environment
Expand All @@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@v2

- uses: cachix/install-nix-action@v13
- uses: cachix/install-nix-action@v22
with:
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz

Expand Down
15 changes: 7 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
FROM node:10.13-alpine

# First part, build the app
WORKDIR /app
COPY package.json /app/
COPY yarn.lock /app/
RUN yarn install

COPY ./ /app/
COPY package.json yarn.lock ./
RUN yarn install
COPY . .

ARG REACT_APP_API_BASE_URL
ENV REACT_APP_API_BASE_URL=$REACT_APP_API_BASE_URL

ARG AUTH_ENABLED
ENV AUTH_ENABLED=$AUTH_ENABLED

# Adds the package version and commit hash
# Adds the package version and commit hash

ARG REACT_APP_VERSION
ENV REACT_APP_VERSION=$REACT_APP_VERSION
Expand All @@ -24,10 +24,9 @@ ENV REACT_APP_COMMIT=$REACT_APP_COMMIT
RUN yarn run build

# Second part, copy the build and server the app using a node express server

RUN cp -r /app/build /app/server/

WORKDIR /app/server

RUN cp -r /app/build ./
RUN npm install

EXPOSE 8080
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Once a new version the UI is ready to deploy simply tag the commit with a string

## Authentication

Authentication is enabled by default in a production build and it cannot be disabled.
Authentication is disabled by default.

In dev mode it can be selectively enabled or disabled depending on the preference.
Simply open the browser console and run the commands
Expand All @@ -150,3 +150,18 @@ Simply open the browser console and run the commands

:warning: **NOTE**
It is suggested to clear local storage and cookies after enabling and disabling authentication. It will help starting a fresh session.


## New login approach
Based on new requirements (see story _IPROD-189_) we need to use Keycloak to authenticate users.
For this purposes *OAuth 2.0 authorization code flow* is used.

### Env variables for OAuth OIDC flow:
- `AUTH_ENABLED` optional and disabled by default. To enable the previous auth flow set it to `true`
- `UI_OIDC_LOGIN_REDIRECT_URL` - URL to redirect user to provide creds and grand permissions (Keycloak)
- `OIDC_TOKEN_PROVIDER_URL` - URL to exchange _authorization code_ for _access token_ (providing also client ID and secret)
- `OIDC_CLIENT_ID` - MCM portal client id (in Keycloak)
- `OIDC_CLIENT_SECRET` - MCM portal client secret (in Keycloak)

### Cookie name:
- `MCM_COOKIE_NAME` - if this env variable is not set, cookie name by default is _'MCM_SESSION'_
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "connection-manager-ui",
"version": "1.6.17",
"version": "2.0.0",
"private": true,
"dependencies": {
"@modusbox/modusbox-ui-components": "1.16.25",
"babel-polyfill": "^6.26.0",
"connected-react-router": "^6.3.2",
"fetch-mock": "^7.3.3",
"history": "^4.9.0",
"js-cookie": "^2",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"moment-timezone": "^0.5.25",
Expand Down
6 changes: 3 additions & 3 deletions server/auth.mock.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const cors = require('cors');
const otplib = require('otplib');
Expand All @@ -19,7 +18,7 @@ const RESPONSES = {
"2faEnabled": true
}
},
'totp_enabled': {
'totp_enabled': {
status: 200,
data: {
"enrolled": true,
Expand All @@ -43,6 +42,7 @@ const RESPONSES = {
};

const app = express();
let response;

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
Expand All @@ -52,7 +52,7 @@ app.post('/login', function (req, res) {

const { username, password } = req.body;
const isValid = username === 'test' && password === 'pass';

if (isValid && !enabled2fa) {
response = RESPONSES.auth_successful;
} else if (isValid && enrolled) {
Expand Down
22 changes: 22 additions & 0 deletions server/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const {
API_BASE_URL,
AUTH_ENABLED,
UI_OIDC_LOGIN_REDIRECT_URL,
OIDC_TOKEN_PROVIDER_URL,
OIDC_CLIENT_ID,
OIDC_CLIENT_SECRET,
MCM_COOKIE_NAME = 'MCM_SESSION',
HTTP_PORT = 8080,
} = process.env;
// validate required env vars

module.exports = {
API_BASE_URL,
AUTH_ENABLED,
UI_OIDC_LOGIN_REDIRECT_URL,
OIDC_TOKEN_PROVIDER_URL,
OIDC_CLIENT_ID,
OIDC_CLIENT_SECRET,
MCM_COOKIE_NAME,
HTTP_PORT,
};
34 changes: 34 additions & 0 deletions server/httpRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const querystring = require('querystring');
const axios = require('axios').default;
const config = require('./config');

const makeOidcPayload = (code) => querystring.stringify({
code,
client_id: config.OIDC_CLIENT_ID,
client_secret: config.OIDC_CLIENT_SECRET,
grant_type: 'authorization_code',
});

const requestOptions = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
};

const exchangeAuthCodeForToken = async (code) => {
const url = config.OIDC_TOKEN_PROVIDER_URL;
const payload = makeOidcPayload(code);

return axios.post(url, payload, requestOptions)
.then((response) => {
const { access_token, expires_in } = response.data; // expires_in in sec
return { access_token, expires_in };
})
.catch((err) => {
const error = `exchange authCode error: ${err.message} - ${JSON.stringify(err.response && err.response.data)}`;
console.error(error, code);
return { error };
});
}

module.exports = {
exchangeAuthCodeForToken,
};
38 changes: 31 additions & 7 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
const express = require('express');
const path = require('path');
const express = require('express');

const { exchangeAuthCodeForToken } = require('./httpRequest');
const {
HTTP_PORT,
API_BASE_URL,
AUTH_ENABLED,
UI_OIDC_LOGIN_REDIRECT_URL,
OIDC_CLIENT_ID,
MCM_COOKIE_NAME
} = require('./config');

const app = express();

app.use(express.static(path.join(__dirname, 'build')));

app.get('/config', function (req, res) {
let config = {
API_BASE_URL: process.env.API_BASE_URL,
AUTH_ENABLED: process.env.AUTH_ENABLED,
app.get('/config', (req, res) => {
const config = {
API_BASE_URL,
AUTH_ENABLED,
UI_OIDC_LOGIN_REDIRECT_URL,
OIDC_CLIENT_ID,
MCM_COOKIE_NAME,
};
console.log('connection-manager-ui server: /config called, returning: ', config);
res.send(config);
});

app.get('/*', function (req, res) {
app.get('/oidc-token', async (req, res) => {
const { code } = req.query;
const response = await exchangeAuthCodeForToken(code);
console.log('oidc response:', response);

res
.status(response.access_token ? 200 : 401)
.json(response);
});

app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(8080);
app.listen(HTTP_PORT, () => { console.log(`Server is running on port ${HTTP_PORT}...`); });
Loading