Skip to content
Snippets Groups Projects
Commit 2ed7ed25 authored by Hahn Axel (hahn)'s avatar Hahn Axel (hahn)
Browse files

update docs

parent 7f3f9d77
Branches
No related tags found
No related merge requests found
Showing
with 628 additions and 0 deletions
## Introduction
By law it is required to enable MFA for users on web aplications having editor or admin permission.
We have some simple webs with Basic authentication where no application internal usermanagement exists.
We wanted to keep the current logon mechanism (because of other dependencies) and touch a minimum of the code. So we didn't want to add a user management in each application.
The idea was born to build a mfa server that implements "a few" mfa methods that can be used by any application we want to connect.
## Reqirements
* PHP 8 (up to PHP 8.4)
* PHP application with a simple user based protection eg. basic authentication.
* A running Mfa server instance
## Installation
### Get source
Go to the web application vendor directory.
Clone this repository.
```txt
git clone https://git-repo.iml.unibe.ch/iml-open-source/mfa-client.git
```
### Configuration
The files in the subdir `src`:
```txt
cd mfa-client/src
```
Copy "mfaconfig.php.dist" to "mfaconfig.php".
Open the mfa server admin and create a new web app. You get an id and a secret for your aplication.
In the config enter the url of api, id and secret.
```php
<?php
return [
"api" => "https://mfa.example.com/api/",
"appid" => "c1cabd22fbdb698861ad08b27de7399a",
"shared_secret" => "p9wjjXSewZq0VkM1t5Sm3ZbI4ATEVetU",
"debug" => false,
];
```
### Activate MFA after logon
This step depends on your code. You need to find a good place to embed the MFA process.
```php
<?php
...
// enable MFA:
include "<APPROOT>/vendor/mfa-client/src/mfa-ensure.php";
...
```
### Give access to user settings on mfa server
If a user is logged in and solves a mfa challenge then he jumps back to theapplication.
You should offer a link to the user that jumps to the mfa server to edit his own settings there.
A good place is the user profile page in your app.
**📌 Example**:
```php
<?php
...
// load class
require "<APPROOT>/vendor/mfa-client/mfaclient.class.php";
// initialize client
$oMfa = new mfaclient();
// $oMfa->debug(true);
// set the user
$oMfa->setUser($this->getUserid());
// show a button; set a complete url where to jump back
echo $oMfa->getButtonSetup(
"<button>MFA settings</button>",
"https://myapp.example.com/profile"
);
...
```
This renders an html form with hidden fields to transmit app id, user id and a HMAC code to the MFA server. This form is valid for 1 minute.
### Troubleshooting
There is a debug mode.
It prints detailed information about the connection to the mfa server and actions on the client.
!!! Error Danger
Do not acrivate it globally for all users on a productive web app!
#### Globally enabled debugging
In the "mfaconfig.php" you can enable a flag "debug":
```php
<?php
return [
"api" => ...
"appid" => ...
"shared_secret" => ...
"debug" => true,
];
```
#### Enable by debug method
Inside php code you can use the method `debug(<bool>)` to enable and disable the debugging.
```php
<?php
require "<APPROOT>/vendor/mfa-client/mfaclient.class.php";
$oMfa = new mfaclient();
// enable debugging
$oMfa->debug(true);
```
In real life applications you add additional conditions, like a user restriction plus a $_GET variable to limit the debug code to you only.
## First access of MFA server
### Introduction
The MFA client handles the connection to the configured MFA server instance.
After logon it forces a successful challenge before a user can continue to the application.
To solve a challenge the browser url changes to the MFA instance and jumps back to the application afterwards.
To reduce the traffic to the MFA server a session variable will be set after successful challenge.
On MFA server is a ttl value how long a solved challenge is marked as OK. When opening a new browser window the MFA server can respond that a valid challenge still exists (and the client sets the session variable again).
### First MFA request
We make a 1st request and are logged in. The MFA client gets the answer from MFA server that the user does not exist and needs a setup.
The MFA client will redirect the user to the MFA server.
### Overview page
Any user of a registered app will be accepted by the mfa server and let's him configure mfa methods.
The setup page shows
* The application with a link to jump back
* The username in the web application
* The ip address of the user when accessing the app
![alt text](screenshot__first_visit__01.png)
If a new user vistes the mfa server then it has no mfa configuration yet. The user needs to setup minimum one of the given methods.
Click on one of the buttons to setup TOTP or email.
### Setup TOTP
When setting up TOTP the system generates a secret.
You see the secret as text and as qr code.
![alt text](screenshot__first_visit__02.png)
Configure your TOTP client to add a new app.
If it is done then enter the current key to finish the setup.
The browser jumps back to the web application.
### Setup email
To setup an email you enter your own email address.
![alt text](screenshot__first_visit__03.png)
The ystem sends a verification code to the entered address and waits for the correct code.
The sent email has a subject with a timestamp including minutes and seconds to identify the email in the inbox.
![alt text](screenshot__first_visit__04.png)
If the entered code is correct the email address will be stored for email mfa. The browser jumps back to the web application.
## Following logons
After a user has added a mfa method and loggs in into the web application... the MFA server responds, that a user profile exists, but mfa challenge is expired.
The MFA client will redirect to the MFA server and offers the 1st found mfa method from the list.
![alt text](screenshot__solve_totp.png)
On the right hand you can switch to other configured mfa methods:
![alt text](screenshot__solve_email.png)
## Setup mfa
After Solving a challenge after login the user is redirected to the application.
Don't forget to offer a setup link to the MFA server.
### Overview
The overview page is nearly the same like on the very first visit.
![alt text](screenshot__setup_overview.png)
On top left the button to jump back to the application is green because a challenge was solved already. Now are all links are enabled.
* My sessions - show stored sessions of the last 4 weeks
* Methods TOTP and email can be viewed and deleted to reconfigure this method
* A challenge can be tested.
### My sessions
![alt text](screenshot__setup_my_sessions.png)
### View TOTP
!!! Warning
Save your TOTP backup codes!
You can see the stored secret for the totp challenge and the backup codes. Used backup codes are marked with an icon and date of its usage.
**Options**:
* You should save your backup codes locally on your computer. They allow you to login when you have no access to your TOTP app.
* You can delete the stored totp secret. Afterwards you can setup a new totp secret for your application
![alt text](screenshot__setup_view_totp.png)
### View Email
**Options**:
* You can delete the stored email. Afterwards you can setup a email
![alt text](screenshot__setup_view_email.png)
docs/40_Usage/screenshot__first_visit__01.png

132 KiB

docs/40_Usage/screenshot__first_visit__02.png

106 KiB

docs/40_Usage/screenshot__first_visit__03.png

60.4 KiB

docs/40_Usage/screenshot__first_visit__04.png

75.7 KiB

docs/40_Usage/screenshot__setup_my_sessions.png

167 KiB

docs/40_Usage/screenshot__setup_overview.png

122 KiB

docs/40_Usage/screenshot__setup_view_email.png

94.6 KiB

docs/40_Usage/screenshot__setup_view_totp.png

129 KiB

docs/40_Usage/screenshot__solve_email.png

91.2 KiB

docs/40_Usage/screenshot__solve_totp.png

87.6 KiB

<html>
<div class="hero">
<h2>MFA client</h2>
PHP class for Mfa using a connection to a mfa server instance
</div>
</html>
👤 Author: Axel Hahn; Institute for Medical Education; University of Bern \
📄 Source: <https://git-repo.iml.unibe.ch/iml-open-source/mfa-client> \
📜 License: GNU GPL 3.0 \
📗 Docs: <https://os-docs.iml.unibe.ch/mfa-client/> (TODO)
**Related projects**:
* MFA server 🌐 <https://git-repo.iml.unibe.ch/iml-open-source/mfa-server>
{
"title": "MFA client (PHP)",
"author": "Axel Hahn",
"tagline": "Mfa client using a connection to a mfa server instance",
"ignore": {
"files": ["30_PHP-client/Plugins/Checks/_skeleton.md"],
"folders": ["99_Not_Ready"]
},
"html": {
"auto_toc": true,
"auto_landing": false,
"date_modified": false,
"jump_buttons": true,
"edit_on_github_": "iml-it/__PROJECT__/tree/master/docs",
"edit_on_": {
"name": "Gitlab",
"basepath": "https://git-repo.iml.unibe.ch/iml-open-source/mfa-server/tree/master/docs"
},
"links": {
"Git Repo": "https://git-repo.iml.unibe.ch/iml-open-source/",
"IML Opensource": "https://os-docs.iml.unibe.ch/"
},
"theme": "daux-blue",
"search": true
}
}
/*
override css elements of daux.io blue theme
version 2024-10-31
*/
:root {
/* Axels Overrides */
--color-text: #234;
--link-color: #228;
--brand-color: var(--color-text);
--brand-background: var(--body-background);
--code-tag-background-color: #f0f3f3;
--code-tag-border-color: #dee;
--code-tag-box-shadow: none;
--hr-color: none;
--pager-background-color: #f8fafa;
--pager-border-color: none;
--search-field-background: none;
--search-field-border-color: none;
--sidebar-background: var(--body-background);
--sidebar-border-color: none;
--sidebar-link-active-background: #f0f4f6;
--toc--inner-border-color: none;
/* Axels custom values */
--axel_bg-toc: #f8fafa;
--axel_bg-toc-head: #f8f8f8;
--axel_brand-background: none;
--axel_brand-pre-background: rgb(255, 0, 51);
--axel_brand-pre-background-hover: rgb(255, 0, 51);
--axel_h1_header: none;
--axel_h1: #111;
--axel_h1-bg: none;
--axel_h1-bottom: 3px solid none;
--axel_h2: #222;
--axel_h2-bg: none;
--axel_h2-bottom: 0px solid #467;
--axel_h2-hero-bottom: 2px solid #912;
--axel_h3: #333;
--axel_h3-bottom: 0px solid #ddd;
--axel_h4: #666;
--axel_h5: #888;
--axel_hero_bg: #faf8f6;
--axel_img-border: 2px dashed #ccc;
--axel_nav-bg: #fcfcfc;
--axel_nav-buttomborder: #ddd;
--axel_pre-background: #faf8f6;
--axel-th-background: #e0e4e8;
--axel-article-nav-border-top: 0px dotted #ddd;
}
.dark {
/* Axels Overrides */
--color-text: #c0c0c0;
--link-color: #88e;
--brand-color: var(--color-text);
--brand-background: var(--body-background);
--body-background: #101418;
--hr-color: none;
--code-tag-background-color_: #bcc;
--search-field-background: none;
--search-field-border-color: none;
--sidebar-background: var(--body-background);
--sidebar-border-color: none;
--sidebar-link-active-background: #333;
--sidebar-link-color: var(--link-color);
/* Axels custom values */
--axel_bg-toc: var(--body-background);
--axel_bg-toc-head: #333;
--axel_brand-background: none;
--axel_brand-pre-background: rgb(255, 0, 51);
;
--axel_brand-pre-background-hover: rgb(255, 0, 51);
;
--axel_h1_header: none;
--axel_h1: #578;
--axel_h1-bg: none;
--axel_h1-bottom: none;
--axel_h2: #467;
--axel_h2-bg: none;
--axel_h2-bottom: 0px solid #256;
--axel_h2-hero-bottom: 2px solid #712;
--axel_h3: #589;
--axel_h3-bottom: 0px solid #333;
--axel_h4: #478;
--axel_h5: #278;
--axel_hero_bg: #242424;
--axel_img-border: 2px dashed #555;
--axel_nav-bg: #242424;
--axel_nav-buttomborder: #555;
--axel_pre-background: #bcc;
--axel-th-background: #203038;
--axel-article-nav-border-top: 0px dotted #234;
}
/* ---------- left side ---------- */
a.Brand::before {
background: var(--axel_brand-pre-background);
color: #fff;
font-family: arial;
font-weight: bold;
padding: 0.5em 0.3em;
content: 'IML';
margin-right: 0.4em;
float: left;
}
a.Brand:hover::before {
background: var(--axel_brand-pre-background-hover);
}
a.Brand {
background: var(--axel_brand-background);
font-size: 200%;
height: 4em;
}
/* ---------- page header: breadcrumb ---------- */
.Page__header {
border: none;
}
.Page__header a {
color: var(--axel_h1_header);
}
.Page__header h1 {
font-size: 1.3em;
}
/* ---------- page content ---------- */
.s-content {
padding-top: 6em;
}
/**
h1::before{color: #aaa;content: 'h1: ';}
h2::before{color: #aaa;content: 'h2: ';}
h3::before{color: #aaa;content: 'h3: ';}
h4::before{color: #aaa;content: 'h4: ';}
h5::before{color: #aaa;content: 'h5: ';}
h6::before{color: #aaa;content: 'h6: ';}
*/
h2::before{color: #888;content: ': : ';}
h3::before{color: #ccc;content: '> ';}
h4::before{color: #ccc;content: '_ ';}
.s-content h1::before{
color: #f00;
content: 'FEHLER: Keine Überschrift 1 in einer Markdown-Datei für Daux verwenden! Mit H2 beginnen!';
content: '!! h1 !! ';
}
.s-content h1 {
background: var(--axel_h1-bg);
color: var(--axel_h1);
font-size: 200%;
font-weight: bold;
margin-bottom: 2em;
margin-top: 2em;
border-bottom: var(--axel_h1-bottom);
}
.s-content h2 {
background: var(--axel_h2-bg);
color: var(--axel_h2);
font-size: 190%;
font-weight: bold;
margin-top: 4em;
border-bottom: var(--axel_h2-bottom);
}
.Page__header > h1:first-of-type {
margin-top: 0em;
margin-left: -1em;
padding-left: 1em;
position: fixed;
min-width: 100%;
background: var(--body-background);
box-shadow: 0 2em 1em var(--body-background);
}
h2:first-of-type {
margin-top: 0em;
}
img{
border: var(--axel_img-border);
border-radius: 1.5em;
padding: 0.7em;
}
.s-content h3 {
background: var(--axel_h3-bg);
color: var(--axel_h3);
font-size: 150%;
font-weight: bold;
margin-top: 3em;
border-bottom: var(--axel_h3-bottom);
}
.s-content > h4 {
color: var(--axel_h4);
font-size: 140%;
font-weight: bold;
margin: 2em 0;
}
.s-content > h5 {
color: var(--axel_h5);
font-size: 135%;
font-weight: bold;
margin: 2em 0;
}
.s-content .TableOfContentsContainer h4 {
margin: 1em 0;
font-size: 110%;
text-align: center;
background-color: rgba(0, 0, 0, 0.1);
padding: 0.3em;
font-weight: bold;
font-family: Arial;
}
ul.TableOfContents a{
color: var(--color-text);
}
.s-content pre {
background: var(--axel_pre-background);
border-radius: 0.5em;
padding: 1rem;
}
/* FIX smaller fnt size in tables */
.s-content table {
font-size: 1em;
}
.s-content table th {
background: var(--axel-th-background);
}
.s-content h3 code {
border: none;
background: none;
}
article nav {
border-top: var(--axel-article-nav-border-top);
margin: 8em 0 5em;
}
.Pager li>a {
padding: 1em 2em;
}
/* ---------- classes ---------- */
.required {
color: #a42;
}
.optional {
color: #888;
}
div.hero {
background: var(--axel_hero_bg);
border-radius: 2em;
padding: 5em 2em;
text-align: center;
margin-bottom: 1.5em;
}
div.hero h2 {
color: var(--color-text);
background: none;
border-bottom: var(--axel_h2-hero-bottom);
font-size: 300%;
margin: 0 0 2em;
}
/* ---------- TOC ---------- */
.TableOfContentsContainer {
background-color: var(--axel_bg-toc);
padding: 0.5em;
}
.s-content .TableOfContentsContainer h4 {
background-color: var(--axel_bg-toc-head);
border-top-left-radius: 1em;
border-bottom: 2px solid var(--axel_bg-toc-bottom-border);
font-size: 1.1em;
margin: 0;
padding: 0.3em;
display: none;
}
.TableOfContentsContainer__content {
border-width: 0px;
font-size: 0.5em;
height: inherit;
overflow: auto;
}
ul.TableOfContents ul {
list-style-type: none;
padding-left: 1em;
}
.TableOfContents a:hover{
text-decoration: underline;
}
@media(min-width:1700px) {
.TableOfContentsContainer {
background: none;
position: fixed;
right: 2em;
top: 4em;
height: 90%;
}
}
/* ----- Icons on links --- */
.EditOn a::before{
content: '✏️ ';
}
.Links a::before {
content: '🌐 ';
}
.Links a[href^="https://os-docs.iml.unibe.ch"]::before {
content: '📗 ';
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment