hydra passwordless

This commit is contained in:
Arnaud Fornerot 2022-08-26 15:13:22 +02:00
parent 7962e2ca9a
commit 21fb28a6f0
8 changed files with 262 additions and 63 deletions

View File

@ -0,0 +1,5 @@
FROM cadoles/hydra-dispatcher-v1:v0.0.0-111-g2e60bdb
COPY hydra/providers.yml /var/www/config/hydra/providers.yml
COPY theme.css /var/www/public/build/theme

View File

@ -0,0 +1,23 @@
hydra:
apps:
- id: passwordless
title:
fr: addresse courriel
en: email address
description:
fr: Authentification via adresse courriel
en: Authentication by email address
icon_url: https://upload.wikimedia.org/wikipedia/commons/4/48/You%27ve_got_mail.png
login_url: http://127.0.0.1:7082/login
consent_url: http://127.0.0.1:7082/consent
logout_url: http://127.0.0.1:7082/logout
attributes_rewrite_rules:
username:
- consent.session.id_token.email
email:
- consent.session.id_token.email
email_verified:
- consent.session.id_token.email_verified

View File

@ -0,0 +1,8 @@
body > section {
background-color: rgba(214, 170, 214, 0.575);
}
input[type="radio"]:checked ~ .app-item {
--tw-shadow: 0 10px 15px -3px rgb(22 78 99 / 0.3), 0 4px 6px -4px rgb(22 78 99 / 0.3);
background-color: rgba(251, 255, 21, 0.507);
}

View File

@ -0,0 +1,19 @@
{
"client_id": "nineskeletor",
"client_name": "Nineskeletor",
"client_secret": "changeme",
"grant_types": [
"authorization_code",
"refresh_token"
],
"jwks": {},
"metadata": {},
"token_endpoint_auth_method": "client_secret_post",
"post_logout_redirect_uris": ["https://127.0.0.1:8000"],
"redirect_uris": ["https://127.0.0.1:8000/oauth2/callback"],
"response_types": [
"code"
],
"logo_uri": "https://upload.wikimedia.org/wikipedia/commons/e/e1/Password.svg",
"scope": "openid"
}

View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
set -u
function create_user_and_database() {
local database=$1
echo " Creating user and database '$database'"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE USER $database;
CREATE DATABASE $database;
GRANT ALL PRIVILEGES ON DATABASE $database TO $database;
EOSQL
}
if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then
echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
create_user_and_database $db
done
echo "Multiple databases created"
fi

View File

@ -1,6 +1,20 @@
version: '3' version: '3'
# Port
# 6379 = redis
# 5432 = postgresql
# 80 = mercure
# 9000 = minio nginx
# 1025 = fake smtp
# 1080 = fake webmail
# 389 = fake ldap
# 636 = fake ldaps
# 6080 = tool phpldapadmin
# 6081 = tool adminer
services: services:
# Service redis pour le stockage des sessions
redis: redis:
image: redis:4.0 image: redis:4.0
container_name: redis container_name: redis
@ -11,25 +25,22 @@ services:
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
postgres: # Service postgresql pour le stockage de la bdd applicative
image: postgres:${POSTGRES_VERSION:-13}-alpine postgresql:
container_name: postgres image: postgres:13-alpine
container_name: postgresql
hostname: postgresql
environment: environment:
POSTGRES_DB: ${POSTGRES_DB:-app} POSTGRES_MULTIPLE_DATABASES: app,hydra
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe} POSTGRES_PASSWORD: ChangeMe
POSTGRES_USER: ${POSTGRES_USER:-symfony} POSTGRES_USER: symfony
ports: ports:
- 5432:5432 - 5432:5432
volumes: volumes:
- db-data:/var/lib/postgresql/data:rw - db-data:/var/lib/postgresql/data:rw
- ./containers/postgresql:/docker-entrypoint-initdb.d
mailer: # Service websocket
image: schickling/mailcatcher
container_name: mailer
ports:
- 1025:1025
- 1080:1080
mercure: mercure:
image: dunglas/mercure image: dunglas/mercure
container_name: mercure container_name: mercure
@ -48,38 +59,7 @@ services:
- mercure_data:/data - mercure_data:/data
- mercure_config:/config - mercure_config:/config
openldap: # Service de stockage Minio
image: osixia/openldap:1.5.0
container_name: openldap
environment:
LDAP_LOG_LEVEL: "256"
LDAP_ORGANISATION: "nineskeletor"
LDAP_DOMAIN: "nine.fr"
LDAP_ADMIN_PASSWORD: "changeme"
LDAP_CONFIG_PASSWORD: "changeme"
LDAP_READONLY_USER: "true"
LDAP_READONLY_USER_USERNAME: "readonly"
LDAP_READONLY_USER_PASSWORD: "readonly"
LDAP_TLS: "false"
volumes:
- /var/lib/ldap
- /etc/ldap/slapd.d
- /container/service/slapd/assets/certs/
ports:
- "389:389"
- "636:636"
phpldapadmin:
image: osixia/phpldapadmin:latest
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: "openldap"
PHPLDAPADMIN_HTTPS: "false"
ports:
- "8080:80"
depends_on:
- openldap
minio1: minio1:
image: minio/minio:RELEASE.2021-01-16T02-19-44Z image: minio/minio:RELEASE.2021-01-16T02-19-44Z
container_name: minio1 container_name: minio1
@ -98,6 +78,7 @@ services:
timeout: 20s timeout: 20s
retries: 3 retries: 3
# Service de stockage Minio
minio2: minio2:
image: minio/minio:RELEASE.2021-01-16T02-19-44Z image: minio/minio:RELEASE.2021-01-16T02-19-44Z
container_name: minio2 container_name: minio2
@ -116,6 +97,7 @@ services:
timeout: 20s timeout: 20s
retries: 3 retries: 3
# Service de stockage Minio
minio3: minio3:
image: minio/minio:RELEASE.2021-01-16T02-19-44Z image: minio/minio:RELEASE.2021-01-16T02-19-44Z
container_name: minio3 container_name: minio3
@ -134,6 +116,7 @@ services:
timeout: 20s timeout: 20s
retries: 3 retries: 3
# Service de stockage Minio
minio4: minio4:
image: minio/minio:RELEASE.2021-01-16T02-19-44Z image: minio/minio:RELEASE.2021-01-16T02-19-44Z
container_name: minio4 container_name: minio4
@ -152,6 +135,7 @@ services:
timeout: 20s timeout: 20s
retries: 3 retries: 3
# Service nginx orchestrateur des minio
nginx: nginx:
image: nginx:1.19.2-alpine image: nginx:1.19.2-alpine
container_name: nginx container_name: nginx
@ -164,6 +148,120 @@ services:
- minio2 - minio2
- minio3 - minio3
- minio4 - minio4
# Service hydra
hydra:
image: cadoles/hydra-v1
container_name: hydra
volumes:
- ./containers/hydra/clients.d:/etc/hydra/clients.d
ports:
- 7080:4444
links:
- postgresql
depends_on:
- postgresql
restart: on-failure
environment:
LOG_LEAK_SENSITIVE_VALUES: "true"
HYDRA_URLS_SELF_ISSUER: http://127.0.0.1:7080
HYDRA_URLS_CONSENT: http://127.0.0.1:7081/consent
HYDRA_URLS_LOGIN: http://127.0.0.1:7081/login
HYDRA_URLS_LOGOUT: http://127.0.0.1:7081/logout
HYDRA_DSN: postgres://symfony:ChangeMe@postgresql:5432/hydra
#HYDRA_WAIT4X_DATABASE_TYPE: postgres
#HYDRA_WAIT4X_DATABASE_DSN: postgres://symfony:ChangeMe@postgresql:5432/hydra
HYDRA_ALLOW_INSECURE: "yes"
HYDRA_LEVEL: debug
hydra-dispatcher:
build:
context: ./containers/hydra-dispatcher
container_name: hydra-dispatcher
links:
- hydra
ports:
- 7081:80
restart: on-failure
environment:
- APP_ENV=dev
- APP_DEBUG=yes
- HYDRA_BASE_URL=http://hydra:4444
- HYDRA_ADMIN_BASE_URL=http://hydra:4445
# url dispatcher
- BASE_URL=http://127.0.0.1:7081
- COOKIE_PATH=/
- DEFAULT_LOCALE=fr
- APP_LOCALES=fr,en
volumes:
- ./containers/hydra-dispatcher/hydra:/var/www/config/hydra:ro
hydra-passwordless:
image: cadoles/hydra-passwordless-v1
container_name: hydra-passwordless
environment:
- HTTP_BASE_URL=http://127.0.0.1:7082/
- SMTP_HOST=mailer
- SMTP_PORT=1025
- SMTP_SENDER_ADDRESS=noreply@hydra.local
- SMTP_SENDER_NAME="Authentification - Passwordless"
- HYDRA_BASE_URL=http://hydra-dispatcher
links:
- hydra
- hydra-dispatcher
- mailer
ports:
- 7082:3000
# Service fake smtp = optionnel
mailer:
image: schickling/mailcatcher
container_name: mailer
ports:
- 1025:1025
- 1080:1080
# Service fake openldap = optionnel si nineskeletor a une synchronisation avec un annuaire
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
environment:
LDAP_LOG_LEVEL: "256"
LDAP_ORGANISATION: "nine"
LDAP_DOMAIN: "nine.fr"
LDAP_ADMIN_PASSWORD: "changeme"
LDAP_CONFIG_PASSWORD: "changeme"
LDAP_READONLY_USER: "true"
LDAP_READONLY_USER_USERNAME: "readonly"
LDAP_READONLY_USER_PASSWORD: "readonly"
LDAP_TLS: "false"
volumes:
- /var/lib/ldap
- /etc/ldap/slapd.d
- /container/service/slapd/assets/certs/
ports:
- "389:389"
- "636:636"
# Service tool phpldapadmin = optionnel dans le cas de la présence d'un fake openldap
phpldapadmin:
image: osixia/phpldapadmin:latest
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: "openldap"
PHPLDAPADMIN_HTTPS: "false"
ports:
- "6080:80"
depends_on:
- openldap
# Service tool adminer = optionnel
adminer:
image: adminer
container_name: adminer
restart: always
ports:
- 6081:8080
volumes: volumes:
db-data: db-data:

View File

@ -214,8 +214,10 @@ class SecurityController extends AbstractController
public function loginOPENID(Request $request, AuthenticationUtils $authenticationUtils) public function loginOPENID(Request $request, AuthenticationUtils $authenticationUtils)
{ {
$state=Uuid::uuid4();
$request->getSession()->set("oauthState",$state);
$callback=$this->generateUrl('app_loginopenidcallback', array(), UrlGeneratorInterface::ABSOLUTE_URL); $callback=$this->generateUrl('app_loginopenidcallback', array(), UrlGeneratorInterface::ABSOLUTE_URL);
$url=$this->getParameter("oauthLoginurl")."?client_id=".$this->getParameter("oauthClientid")."&redirect_uri=".$callback."&response_type=code&state=12345678&scope=openid"; $url=$this->getParameter("oauthLoginurl")."?client_id=".$this->getParameter("oauthClientid")."&redirect_uri=".$callback."&response_type=code&state=".$state."&scope=openid";
return $this->redirect($url); return $this->redirect($url);
} }
@ -236,15 +238,20 @@ class SecurityController extends AbstractController
"client_id" => $this->getParameter("oauthClientid"), "client_id" => $this->getParameter("oauthClientid"),
"client_secret" => $this->getParameter("oauthClientsecret"), "client_secret" => $this->getParameter("oauthClientsecret"),
]; ];
$response=$this->apiservice->run("POST",$apiurl,$query); $response=$this->apiservice->run("POST",$apiurl,$query,null,"form");
if(!$response||$response->code!="200") return $this->logout($request); if(!$response||$response->code!="200") die("pb openid 01");
$token=$response->body->access_token; $accesstoken=$response->body->access_token;
$request->getSession()->set("oauthToken",$token); $accesstokentype=$response->body->token_type;
$îdtoken=$response->body->id_token;
$request->getSession()->set("oauthAccesstoken",$accesstoken);
$request->getSession()->set("oauthIdtoken",$îdtoken);
$apiurl = $this->getParameter("oauthUserinfo"); $apiurl = $this->getParameter("oauthUserinfo");
$response=$this->apiservice->run("GET",$apiurl,null,["Authorization"=>"token ".$token]);
if(!$response||$response->code!="200") return $this->logout($request); $response=$this->apiservice->run("GET",$apiurl,null,["Authorization"=>$accesstokentype." ".$accesstoken]);
if(!$response||$response->code!="200") die("pb openid 02");
$attributes=json_decode(json_encode($response->body), true); $attributes=json_decode(json_encode($response->body), true);
@ -346,14 +353,21 @@ class SecurityController extends AbstractController
public function logoutOPENID(Request $request) { public function logoutOPENID(Request $request) {
$token=$request->getSession()->get("oauthToken"); $accesstoken=$request->getSession()->get("oauthAccesstoken");
$idtoken=$request->getSession()->get("oauthIdtoken");
$state=$request->getSession()->get("oauthState");
$this->tokenstorage->setToken(null); $this->tokenstorage->setToken(null);
$request->getSession()->invalidate(); $request->getSession()->invalidate();
$url=$this->getParameter("oauthLogouturl"); $url=$this->getParameter("oauthLogouturl");
if($url) { if($url) {
$url.="?id_token_hint=$token&scope=openid&post_logout_redirect_uri=http://127.0.0.1:8000"; $callback=($request->isSecure()?"https://":"http://").str_replace("//","/",$this->getParameter("appWeburl").$this->getParameter("appAlias").$this->generateUrl('app_home'));
$callback=substr($callback, 0, -1);
$url.="?id_token_hint=$idtoken&scope=openid&state=$state&post_logout_redirect_uri=$callback";
return $this->redirect($url); return $this->redirect($url);
} else return $this->redirect($this->generateUrl("app_home")); } else return $this->redirect($this->generateUrl("app_home"));
} }

View File

@ -19,12 +19,25 @@ class ApiService
return \Unirest\Request\Body::json($array); return \Unirest\Request\Body::json($array);
} }
public function run($method,$url,$query,$header=null) { public function run($method,$url,$query,$header=null,$content="json") {
// Entete // Entete
$headerini = [ switch($content) {
'Accept' => 'application/json', case "json":
'Content-Type' => 'application/json', $headerini = [
]; 'Accept' => 'application/json',
'Content-Type' => 'application/json',
];
if($query) $query = \Unirest\Request\Body::json($query);
break;
case "form":
$headerini = [
'Accept' => 'application/json',
'Content-Type' => 'application/x-www-form-urlencoded',
];
if($query) $query = \Unirest\Request\Body::form($query);
break;
}
if($header) $header=array_merge($headerini,$header); if($header) $header=array_merge($headerini,$header);
else $header=$headerini; else $header=$headerini;
@ -42,9 +55,6 @@ class ApiService
\Unirest\Request::proxy($proxyHost, $proxyPort, CURLPROXY_HTTP, true); \Unirest\Request::proxy($proxyHost, $proxyPort, CURLPROXY_HTTP, true);
} }
//if($query) $query = \Unirest\Request\Body::json($query);
if($query) $query = http_build_query($query);
$response = false; $response = false;
switch($method) { switch($method) {
case "POST": case "POST":