Projet : Kinaps
Description et contexte
Kinaps is a revolutionary collaborative desktop environment which blurs the boundaries between people and devices. Designed to leverage the power of modern multi-touch screens, Kinaps makes collaboration more fun and productive than ever. - Kinaps
Nous avons rejoint le projet Kinaps dans le but de leur apporter la compréhension de l’environnement Teams, qui est encore très récent, et de leur fournir un Proof-Of-Concept (POC) de l’intégration de leur service dans une application Teams. En effet, suite au COVID-19, les entreprises ont dû s’adapter et mettre en place le télétravail, ce qui a eu pour conséquence d’augmenter l’utilisation du service de collaboration professionnel Microsoft Teams. Kinaps voulant offrir leur service au plus grand nombre de clients possibles, ont décidé de s’intégrer à l’environnement Teams.
Par contre nous n’avons pas directement été engagés par Kinaps, mais plutôt par un équipe d’adjoints scientifiques de la HES-SO Valais-Wallis au technopôle de Sierre. Cette équipe était composée de 2 personnes : Zhan Liu et Fabian Cretton qui étaient nos principaux interlocuteurs du projet. Ceci a donc créé une relation triangulaire entre nous, Kinaps et l’équipe d’adjoints scientifiques, ce que je n’avais jamais expérimenté auparavant et qui a apporté un niveau de complexité supplémentaire. Au sein de Kinaps nous avons eu contact avec 2 personnes : le CEO, Alain Crevoisier et le CTO, Rémy Berrebi.
Le rapport de projet complet est disponible ici
Mon rôle
Dans ce projet, au niveau de la réalisation, j’ai eu 2 tâches principales :
- Intégrer l’expérience Single Sign-On (SSO).
- Faire les appels API vers Microsoft et Kinaps.
Les détails sont disponibles plus bas dans la partie Mes Réalisations.
Fonctionnement général de l’application
L’application est composée de 2 parties principales :
- La configuration est obligatoire pour toute nouvelle application de type onglet dans Teams et permet de configurer certains éléments avant de lancer le coeur de l’application. C’est par exemple là que le SSO a été implémenté et que l’utilisateur peut choisir s’il veut créer un nouveau board (1) ou afficher un board existant (2).
- L’application de type onglet qui va afficher le board choisi ou créé par l’utilisateur.
Mes apprentissages
Ce projet a été très enrichissant pour moi car techniquement il m’a permis de découvrir le fonctionnement d’une application de type onglet dans Teams et du Single Sign-On (SSO) ainsi que de pratiquer de nombreux appels API vers Microsoft et Kinaps. Le début du projet a été lent au niveau de la réalisation mais nous a nécessité beaucoup de temps d’apprentissage. J’ai dû apprendre à parcourir énormément de documentation et d’exemples (souvent contradictoires) pour en extraire les informations qui m’étaient pertinentes et utiles. Nous avons également dû coordonner nos efforts avec Maxime pour avancer en parallèle sur le projet en partageant nos apprentissages et nos découvertes.
La gestion de projet a également été très enrichissante car nous avons dû gérer une relation triangulaire entre nous, Kinaps et l’équipe d’adjoints scientifiques et gérer l’intégration de nos collègues Joanna et Hugo en fin de projet pour qu’ils puissent également contribuer au projet via des appels API. Le travail en binôme avec Maxime a également été très enrichissant car nous avons dû coordonner nos efforts et partager nos apprentissages pour avancer en parallèle sur le projet.
Finalement, la réalisation du rapport de projet qui devait intégrer le rapport pré-existant écrit par Zhan et Fabian a été un bon exercice de collaboration et de rédaction de rapport et m’a permis de voir à quoi ressemblait un rapport de projet professionnel.
Gestion de projet
Comme mentionné ci-dessus, notre projet était organisé de façon tripartite. Cette organisation était tout à fait nouvelle pour moi et m’a permis de tester une nouvelle manière de procéder avec un intervenant traduisant les besoins du client final pour nous.
Un exemple de problème lié à cette organisation tripartite a été liée aux appels API. En effet, après avoir implémenté nos premiers appels API nous avons fait face à un problème lié aux CORS (Cross-Origin Resource Sharing), un réglage fait au niveau du serveur Kinaps auquel nous n’avions pas accès, qui empêchait les appels API de fonctionner. Après avoir partagé le problème détaillé avec un des adjoints scientifiques, j’ai été renvoyé de l’un vers l’autre pendant plusieurs jours sans que personne ne puisse m’aider. Finalement la solution est arrivée lorsque j’ai pu rejoindre un appel avec le CTO de Kinaps qui a pu résoudre notre problème en 15 minutes. Mais ce problème a aussi montré qu’il n’était pas dû à une mauvaise communication de ma part, mais plutôt à la nature complexe de cette organisation. En effet j’avais clairement documenté la problématique et les recherches que j’avais faites pour la résoudre, mais je n’avais pas un accès direct à la personne qui pouvait m’aider à le résoudre.
Au niveau de l’outil de gestion de projet, nous avons tout d’abord commencé avec Azure DevOps où nous avons réalisé un Product Backlog et tenté d’implémenter des sprints réguliers. Malheureusement, la complexité du projet qui nous a demandé un grand temps de recherche et d’apprentissage nous a empêché de respecter les sprints. Nous avons donc décidé de passer à une nouvelle approche lorsque Maxime a pris le rôle de chef de projet en Novembre. Nous avons alors commencé à utiliser un board Kanban et avec l’aide de Zhan, nous avons défini des objectifs hebdomadaires. Du fait que la compréhension avait beaucoup avancé à ce moment là et que la méthode de gestion de projet convenait mieux au projet, nous avons dès lors pu avancer plus rapidement et efficacement.
Mes Réalisations
Single Sign-On
Le Single Sign-On est un système d’authentification qui permet à un utilisateur de se connecter à plusieurs applications avec un seul identifiant. Dans notre cas, nous avons utilisé le protocole OAuth 2.0 via la Microsoft Authentication Library (MSAL) pour permettre à un utilisateur de se connecter à l’application Teams et d’accéder à Kinaps sans avoir à se reconnecter.
Cette expérience a été pour moi une initiation au SSO car je n’en avais jamais fait auparavant. J’ai donc dû me renseigner sur le sujet à l’aide de la documentation Microsoft et de recherches sur internet. Ces recherches ont été complexes et longues car Microsoft fournit énormément de documentation qui est souvent très technique et difficile à comprendre. Elle est même régulièrement contradictoire car plusieurs sources officielles fournissent plusieurs méthodes et exemples différents. Il m’a donc fallu en choisir une et l’adapter à notre cas.
Configuration du portail Azure
Pour commencer, il a fallu créer une application dans le portail Azure. Ceci nous permet de gérer les autorisations, l’authentification et les permissions nécessaires au bon fonctionnement de l’application. On doit par exemple y configurer les URL de redirection, les permissions d’accès aux ressources, les certificats, les tokens d’identité et d’accès, etc. Toutes ces configurations se retrouvent ensuite résumées dans un fichier JSON appelé manifest.json :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"optionalClaims": {
"idToken": [
{
"name": "email",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "family_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "given_name",
"source": null,
"essential": false,
"additionalProperties": []
}
],
}
Dans cet extrait on retrouve la configuration des optionalClaims qui permettent de récupérer les informations de l’utilisateur. Dans notre cas, nous avons besoin de son adresse email, son nom et son prénom.
Ce manifeste m’a posé beaucoup de problèmes car il est très mal documenté et il est difficile de trouver des exemples concrets. J’ai donc dû faire beaucoup de recherches et de tests pour comprendre comment le configurer correctement. Par exemple, aux débuts du projet, après avoir configuré tous les éléments liés au tokens d’identité via le portail Azure, je voulais en récupérer un depuis l’application, mais à chaque tentative toutes mes configurations étaient réinitialisées dans le portail. Après beaucoup de tests et de recherche, j’ai compris qu’il fallait dupliquer les changements du manifeste en ligne dans le manifeste local du projet appelé aad.manifest.json. En effet, lorsque l’application était lancée, le manifeste local écrasait le manifeste en ligne sans avertissement et supprimait donc tous mes changements. Ceci est un des nombreux exemples de problèmes rencontrés qui n’étaient documentés nulle part.
Authentification et récupération du token
Une fois la configuration du portail Azure terminée, il a fallu implémenter l’authentification et la récupération du token dans l’application. Au niveau du code j’ai créé le fichier appelé authUtils.api.tsx qui gère l’authentification Microsoft et la récupération du token d’idendité via la fonction getIdToken() :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
export const getIdToken = async (
myMSALObj: Msal.UserAgentApplication
): Promise<true | false> => {
// Récupérer le token stocké dans le localStorage
const storedToken = localStorage.getItem("stringToken");
const decodedToken = storedToken ? JSON.parse(storedToken) : null;
// Check if token is expired
const isTokenExpired = () => {
return decodedToken.exp ? decodedToken.exp < Date.now() / 1000 : true;
};
// If token is not present or expired, initiate a new login
if (!storedToken || isTokenExpired()) {
console.error(
"ID Token manquant ou expiré, nouvelle demande d'authentification"
);
// If token is expired or not present, clear cache before new login sequence
await clearCache();
// Get context before new login sequence
await getContext();
// Initier une nouvelle demande d'authentification pour obtenir un nouveau token
try {
const loginRequest: Msal.AuthenticationParameters = {
scopes: ["openid", "profile", "User.Read"],
};
const loginResponse = await myMSALObj.loginPopup(loginRequest);
// Stocker le nouveau token et les variables d'utilisateur dans le localStorage
const newIdToken = jwtDecode<MyJwtPayload>(
loginResponse.idToken.rawIdToken
);
// Stocker les tokens et les variables d'utilisateur dans le localStorage
const rawIdToken = loginResponse.idToken.rawIdToken;
const stringToken = JSON.stringify(newIdToken);
const parsedToken = newIdToken ? JSON.parse(stringToken) : null;
localStorage.setItem("rawIdToken", rawIdToken);
localStorage.setItem("parsedToken", parsedToken);
localStorage.setItem("stringToken", JSON.stringify(newIdToken));
localStorage.setItem("userEmail", parsedToken.email);
localStorage.setItem("userGivenName", parsedToken.given_name);
localStorage.setItem("userFamilyName", parsedToken.family_name);
const expiration = parsedToken.exp.toString();
localStorage.setItem("exp", expiration);
return true;
} catch (error) {
console.error(
"Erreur lors de la nouvelle demande d'authentification",
error
);
// Gérer l'erreur d'authentification, rediriger l'utilisateur ou prendre d'autres mesures nécessaires
return false;
}
} else {
return true;
}
};
Dans cette fonction j’utilise la librairie Microsoft Authentication Library (MSAL) qui permet de gérer l’authentification et la récupération du token via la configuration des paramètres demandés puis l’appel de la fonction loginPopup(), qui initie une authentification dans un pop-up. Une fois l’authentification réussie, le token est stocké dans le localStorage et peut être utilisé pour l’authentification avec le serveur Kinaps. J’ai également géré l’expiration du token qui se fait automatiquement après une heure, ainsi que le stockage d’informations de l’utilisateur dans le localStorage afin qu’elles soient facilement accessibles.
Appels API vers Kinaps
Une fois l’authentification Microsoft terminée et pour pouvoir terminer le SSO, il m’a fallu implémenter l’appel API vers Kinaps qui gère l’authentification de leur côté. Ceci a été fait via la fonction getUser() :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
export const getUser = async (): Promise<any> => {
// Expected result example :
/*
{
"_id": "6531472d85045291d62900ef",
"profile_image": null,
"login_name": "moodle_stud",
"first_name": "Moodle",
"last_name": "Stud",
"display_name": "moodle_stud"
}
*/
const rawIdToken = localStorage.getItem("rawIdToken");
var myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${rawIdToken}`);
var requestOptions = {
method: "GET",
headers: myHeaders,
credentials: "include" as RequestCredentials,
redirect: "follow" as RequestRedirect,
};
try {
const response = await fetch(
"https://kinaps-dev-wp5.iict.ch/api/v_3/users/microsoft",
requestOptions
);
const result = await response.json();
console.log("getUser result : ", result);
return result;
} catch (error) {
console.log("Error in getUser : ", error);
return;
}
};
Cette fonction récupère l’ID Token reçu de Microsoft et l’envoie à Kinaps afin de terminer le SSO. De leur côté, Kinaps vérifie l’authenticité du token et renvoie les informations de l’utilisateur s’il existe, ou créé un nouveau compte avant de renvoyer les informations. Une fois cette étape terminée, le SSO est terminé et l’utilisateur n’a eu qu’a se connecter manuellement une fois lors du pop-up mentionné plus haut.
J’ai également implémenté d’autres appels API vers Kinaps permettant de créer une structure de dossier et des boards selon les demandes de nos clients. Ces appels API ont été implémentés dans les fichiers Board.api.tsx et Folder.api.tsx.