Table des matières

Authentification pour les api rest

Rappel

Principe général

Dans tous les cas, le principe est qu'à chaque appel au backend :

  1. on identifie le user de l'appel
  2. on vérifie qu'il a les droits d'appeller la méthode appelée

Généralement, les api rest sont stateless, que ce soit avec ou sans protection. (Le mode statefull introduit des risques de sécurité, qu'il faut contrer en ajouter d'autres mécanismes comme le CSRF).

Mise en oeuvre possible d'authentifications pour des service rest

1.Authentication Basic

  Les solutions qui suivent sont toutes à base de token (jetons) générés et mémorisés par le backend. 
  En général, ces tokens sont uniques, et ne sont pas partagés entre les clients, chaque token représentant justement la session d'un client.
  Le client envoie ce token dans un header http (2 premiers exemples), ou dans le header http des cookies (la session est automatiquement maintenue par le navigateur)

2.Authentication Oauth compliant

Remarque : OAuth 2.0 est une norme complète, qui définit exactement comment les échanges doivent se passer, quelles sont les structures de données, etc … https://oauth.net/2/

3.Authentication avec Token customisé

Exemple qui définit le minimum de conf pour faire de l'authentification avec un jeton situé dans le header http : x-auth-token.
Pour cela, il utilise la classe : org.springframework.session.web.http.HeaderHttpSessionStrategy (voir javadoc) pour instancier la classe de configuration HttpSessionConfig.
Cette dernière est décorée avec @Configuration et @EnableSpringHttpSession =⇒ Utilise le concept de session http wrappé par le framework spring.

Remarque trouvée sur un forum :
Authorization is the primary header used by clients to authenticate against peers in HTTP as foreseen in RFC 7235.
Through the IANA HTTP Authentication Scheme Registry (see also: RFC 7235, sec. 5.1) you will find the Bearer scheme (defined in RFC 6750), which is closely tied to OAuth 2.0.
X-Auth-Token is pretty much providing a shortcut here as it (presumably) does not rely on either OAuth or the HTTP authentication framework.

Please note that with X-Auth-Token being an unregistered header, it is subject to no formal specification and its presence and content is always tied to a respective application. No general assumptions can be made on it.

Dans cet exemple, il y a tout le reste à mettre en place : lecture/serialisation du token, chargement de l'utilisateur pour le mettre dans

4.Authentication Authent + statefull + CSRF

5. Autres mécanismes

Pour être complet sur les mécanismes d'authentification envisageables : http://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml

6. Remarques

A priori, csrf n'est pas nécessaire quand on utilise un token dans Authorization, parce que le navigateur n'authentifie pas automatiquement la requête ( comme c'est le cas avec le cookie JESSIONID de java ). https://security.stackexchange.com/questions/170388/do-i-need-csrf-token-if-im-using-bearer-jwt

7.Notion de refresh_token

	Bien souvent, les tokens générés par les backend ont une date d'expiration ; OAuth 2.0 définit un mécanisme, qui est le suivant : 
	Quand le backend renvoie un token suite à un login/password successfull, il renvoie avec ce token, un refresh_token ( https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/ )
	Cela permet au frontend de renouveler son token quand il voit qu'il arrive à expiration (sans avoir besoin d'une intervention de l'utilisateur).
	L'implémentation doit suivre ceci : https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
Autre lien : 
	Authentification pour les websocket : 
		https://github.com/spring-projects/spring-framework/blob/master/src/docs/asciidoc/web/websocket.adoc#token-authentication

Spring security

Authentication

<sxh java> org.springframework.security.core.Authentication </sxh> est un objet spring qui peut représenter tantot les credentials pour la 1ere étape d'authentification, ou bien le jeton passé à chaque requete http.
Ci-dessous je ne présente que la partie où il sert de jeton.

<sxh java> UsernamePasswordAuthenticationToken </sxh> hérite de Authentication.
Cet objet est peuplé par une classe de filtre, située vers le début de la chaine de traitement de chaque requetes http ; puis cette classe appelle AuthenticationManager.authenticate(Authentication)

<sxh java> AuthenticationManager.authenticate(Authentication) </sxh> Globalement, la session HTTP porte un objet Authentication, qui permet de savoir si l'utilisateur est authentifié, et connaitre le détail de l'utilisateur.

The AuthenticationManager is just an interface, so the implementation can be anything we choose

The default implementation in Spring Security is called   'ProviderManager'   and rather than handling the authentication request itself, 
it delegates to a list of configured AuthenticationProvider s, each of which is queried in turn to see if it can perform the authentication. 
Each provider will either throw an exception or return a fully populated Authentication object.
Identification à chaque requete via header bearer 
    NazaTokenAuthenticationChainFilter.doFilterInternal() 
		--> ProviderManager.authenticate(extractedToken) 
			--> NazaTokenAuthenticationProvider(implement AuthenticationProvider).authenticate(extractedToken) 
				--> NazaTokenAuthenticationProvider.retrieveUser(extractedToken) 
					--> ... on retrouve le user ou on envoie une AuthenticationException ...
					
				Si aucune exception, alors à un moment dans ProviderManager, un code proche du suivant est appelé pour setter le user du thread exécutant la requete http : 
					SecurityContextHolder.getContext().setAuthentication(authentication);

Chaque AuthenticationProvider.authenticate(Authentication) peut lancer une exception AuthenticationException qui est la classe abstraite de BadCredentialsException, AccountExpiredException, en gros toute exception qui invalide l'autorisation).

Droits d'accès

<sxh java>

	@Secured("ROLE_USER1")
	@RequestMapping(value = "/test-with-role-user1-secured", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public void testWithRole1Secured() { ... }
	@Secured("ROLE_USER2")
	@RequestMapping(value = "/test-with-role-user2-secured", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public void testWithRole2Secured() { ... }
	@PreAuthorize("hasRole('USER1')")
	public void testWithRole1PreAuthorize() { ... }	  

</sxh>