Objectif
Dans ce tuto on va voir comment sécuriser une API Java avec OpenID connect via keycloak, ainsi qu’un exemple d’utilisation avec un front-end développé avec Vue 3.
OpenID connect / Oauth, nous permet de choisir parmis plusieurs « flow » d’utilisation selon notre cas d’usage, dans cet exemple on va utiliser le flow « Authorization code ».
voici l’architecture qu’on va utiliser pour cet exemple :
Jargon Oauth / OpenID connect
- client: l’application souhaitant accéder à une API sécurisée avec Oauth
- flow: mode d’optention de token
- token: code à rajouter aux entêtes de toutes les requetes pour accéder à une API sécurisée
- resource: API sécurisée, accessible si un token valide est présent dans la requête.
- realm: dans keycloak, un realm représente un groupe de configurations (clients, users …)
Description du flow Authorization code
Authorization code est un type de flow (ou grant type), est un des modes de fonctionnement proposés par les specs Oauth 2 et OIDC permettant d’accéder à des APIs sécurisées, il est principalement utilisé quand votre application est directement utilisée par l’utilisateur final, par exemple une application web, application mobile ou desktop.
oauth nous permet de choisir d’autres types d’accès plus adaptés à votre cas d’usage, vous pourrez par exemple utiliser le flow client credentials flow dans le cas d’une communication entre deux serveurs.
Etape 1: Démarrer keyclaok avec docker
On va commencer par démarrer une instance de keycloak en local en utilisant docker pour faire vite :
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:20.0.1 start-dev
le téléchargement et lancement de l’image devrait être assez rapide, passons maintenant à la configuration de notre serveur keycloak.
Etape 2: Configurer keycloak
Rendez vous à la « page d’accueil de keycloak » sur http://localhost:8080/.
cliquez sur administration console puis authentifiez vous avec les credentials que vous avez fournit durant le lancement de keycloak, si vous avez juste copié/collé la commande dans l’article alors c’est :
- username: admin
- password: admin
Vous devrez voir maintenant la page d’accueil de keycloak, vous êtes prêt maintenant pour l’étape suivante.
Créer un realm
Keycloak permet de gérer plusieurs configurations en même temps, ça veux dire qu’un serveur keycloak peut potentiellement sécuriser plusieurs APIs / applications complétement différentes de manière complétement isolée.
dans le jargon keycloak ce groupe de configurations isolés sont nommés « realms », créyons alors notre realm en cliquant sur le bouton create realm.
pour cet exemple on va nommer notre realm : myrealm
N’oubliez de choisir le realm qu’on vient de créer avant de passer aux étape suivantes:
Créer un client
On va maintenant ajouter un client ce qui va représenter l’application souhaitant communiquer avec l’API à travers keycloak.
dans le menu à gauche cliquez sur clients puis sur le bouton create client.
entrez le nom du client, pour cet exemple j’ai utilisé myclient, puis cliquez sur next
Activez client authentication, c’est necessaire pour pouvoir spécifier le paramètre redirect_uri necessaire pour l’intégration avec une intégration web.
à noter que le flow « Authorization code » correspond dans keycloak au standard flow qui est coché par défaut lors de la création d’un client.
maintenant que le client est créé, on va configurer les champs redirect_uri et web origins avec l’url de notre front.
avant de passer à l’étape suivante copiez le champ client_secret qui sera utilisée plus tard.
cliquez sur save pour enregistrer puis passons à l’étape suivante.
Créer un utilisateur
passons maintenant à la création d’un utilisateur, contrairement au client qui représente l’application front dans notre exemple, un utlisateur est comme son nom l’indique représente l’utilisateur qui va utiliser l’application.
remplissez le champ username, puis allez sur l’onglet credentials pour créer un mot de passe pour l’utilisateur.
Etape 3: Créer une application securisée
On va maintenant créer notre api sécurisée (resource server dans le jargon oauth), pour ce faire on va se rendre sur start.spring.io, j’ai choisi d’utiliser maven pour gérer les dépendances et java 11 parce que c’est ce que j’ai sur mon poste 🙂
Cliquez sur Generate pour télécharger le projet généré, dézippez le puis ajoutez la dépendance oauth dans la section dependencies , ça devrait ressembler à ça :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
</dependencies>
mettez à jour les dépendances Maven, vous devrez voir à gauche un bouton maven vertical, ça devrait ouvrir un paneau avec le bouton pour forcer la mise à jour des dépendances.
Les dépendances sonts prêtes on peut maintenant ajouter la configuration nécessaire pour sécuriser notre API via keycloak.
server.port=9090
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/realms/myrealm
spring.security.oauth2.resourceserver.jwt.jwk-set-uri = http://localhost:8080/realms/myrealm/protocol/openid-connect/certs
app.security.cors.origin=http://localhost:3000
On va tester cette api depuis un front-end, du coup on va désactiver les règles CORS :
// file: src/main/java/com/bmar1/demosecureapioidc/WebSecurityConfig.java
package com.bmar1.demosecureapioidc;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.stereotype.Component;
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests(configurer ->
configurer
.antMatchers("/error")
.permitAll()
.anyRequest()
.authenticated()
)
.oauth2ResourceServer().jwt();
}
}
tout est configuré, on peut maintenant créer notre endpoint de test :
// chemin: src/main/java/com/bmar1/demosecureapioidc/controllers/SecureApiController.java
package com.bmar1.demosecureapioidc.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = {"${app.security.cors.origin}"})
@RestController
public class SecureApiController {
@GetMapping("/api/test")
public String getMessage() {
return "TEST OK - Bravo";
}
}
Démarrez l’application puis testez l’endpoint sur le navigateur sur localhost:9090/api/test vous devrez voir une page d’erreur 401, ce qui est normal, cet endpoint necessite maintenant un token fournit par le serveur keycloak précédement configuré.
Etape 4: la partie front avec Vue 3
J’ai créé une petite demo en utilise vue 3, je l’ai créé en utilisant vite,et vous pouvez trouver trouver l’exemple complet sur github.
clonez le projet puis configurez les paramètres client_id et client_secret dans le fichier .env.
VITE_CLIENT_ID=myclient
VITE_CLIENT_SECRET=remplacer_par_votre_client_secret
installez les dépendances :
npm i
Puis lancez ll’application front-end :
npm run dev
ceci va démarrer un serveur de développement sur http://localhost:3000.
Déroulement de la démo
Voici un schema montrant comment le front intéragit avec keycloak, l’api:
Pour reproduire l’exemple chez vous, cliquez sur le bouton bleu au centre de la page, ceci va vous rediriger vers le serveur d’authentification pour demander un authorization code.
Ceci va vous rediriger vers keycloak qui va vous donner un authorization code si vous êtes authentifié, dans le cas contraire le serveur va vous inviter à vous authentifier :
Authentifiez vous avec l’utilisateur que vous avez créé durant la configuration de keycloak.
Après l’authentification le serveur keycloak va vous rediriger vers la page configurée dans le paramètre redirect_uri du client dans keycloak en rajoutant le code d’authorisation en paramètre de l’url. l’rl en question va ressembler à ça :
le text survolé dans la capture d’écran est un exemple d’authorization code renvoyé par keycloak.
maintenant qu’on a le code d’authorization à usage unique, on va l’échanger contre un access token, et c’est ce token qu’on va utiliser pour appeler notre api sécurisée
voici le bout de code qui fait ça dans notre exemple :
fichier : src/views/Authentified.vue
onMounted(async () => {
if (route.query.code) {
const data = await getToken(route.query.code as string);
if(data && data.access_token) {
accessToken.value=data.access_token;
} else {
router.push('/');
}
} else {
router.push('/');
}
});
Cette page n’est accessible qu’après une redirection par keycloak, du coup on échange le code contre un token dès que la page est chargée, ce token est après stockée dans une variable pour le réutiliser plus tard.
On peut maintenant appler l’api en utilisant le token, puis afficher le resultat sur la page.
...
apiResult.value = await callSecuredAPI(accessToken.value);
...
export const callSecuredAPI = async (accessToken: string) => {
try{
const response = await axios.get('http://localhost:9090/api/test', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return response.data;
} catch(error) {
console.log(error);
}
}
...
Vous pouvez accéder au code complet sur github.
Je n’ai pas encore mis en place une section commentaire, du coup n’hésitez pas à m’envoyer un email à b.marwane@gmail.com si vous avez une question sur le sujet.