L'authentification des applications mobiles est devenue un enjeu crucial dans le développement moderne. OAuth s'est imposé comme le standard de facto pour sécuriser l'accès aux APIs, particulièrement dans l'écosystème .NET. Dans cet article, nous allons explorer en détail l'implémentation d'OAuth pour l'authentification mobile avec ASP.NET Core et C#.
Comprendre OAuth dans le contexte .NET
OAuth 2.0 est un protocole d'autorisation qui permet à une application d'accéder aux ressources d'un utilisateur hébergées sur un autre serveur, sans avoir besoin de ses identifiants. Dans le contexte .NET, plusieurs composants natifs facilitent son implémentation :
- Microsoft.AspNetCore.Authentication.JwtBearer
- IdentityServer4 ou Duende IdentityServer
- Microsoft.Identity.Web
Implémentation du serveur d'autorisation
Commençons par configurer un serveur d'autorisation avec ASP.NET Core :
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
}
Configuration du client mobile
Pour le client mobile, nous utiliserons le package NuGet IdentityModel :
public class OAuthClient
{
private readonly HttpClient _httpClient;
private readonly OAuthSettings _settings;
public async Task GetTokenAsync(string username, string password)
{
var disco = await HttpClientDiscoveryExtensions
.GetDiscoveryDocumentAsync(_httpClient, _settings.Authority);
if (disco.IsError)
throw new Exception(disco.Error);
var tokenResponse = await _httpClient.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = _settings.ClientId,
ClientSecret = _settings.ClientSecret,
UserName = username,
Password = password,
Scope = "api1"
});
return tokenResponse;
}
}
Sécurisation des API
La sécurisation des endpoints API se fait via des attributs d'autorisation :
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class SecureController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return Ok(new { message = $"Données sécurisées pour l'utilisateur {userId}" });
}
}
Bonnes pratiques de sécurité
- Utilisez HTTPS pour toutes les communications
- Stockez les tokens de manière sécurisée (KeyChain pour iOS, KeyStore pour Android)
- Implémentez le refresh token pattern
- Validez toujours les JWT côté serveur
Gestion des erreurs
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (SecurityTokenExpiredException)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(new
{
error = "Token expiré"
});
}
catch (SecurityTokenValidationException)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(new
{
error = "Token invalide"
});
}
}
}
Tests d'intégration
public class AuthenticationTests : IClassFixture>
{
private readonly WebApplicationFactory _factory;
[Fact]
public async Task AuthenticatedEndpoint_WithValidToken_ReturnsSuccess()
{
// Arrange
var client = _factory.CreateClient();
var token = await GetValidTokenAsync();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Act
var response = await client.GetAsync("/api/secure");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
Considérations de performance
Pour optimiser les performances :
- Mettez en cache les discovery documents
- Utilisez la validation de token asynchrone
- Implémentez un mécanisme de rate limiting
public class TokenCache
{
private static readonly MemoryCache _cache = new MemoryCache(
new MemoryCacheOptions());
public static async Task GetOrCreateAsync(
string key,
Func> factory)
{
return await _cache.GetOrCreateAsync(key, async entry =>
{
var token = await factory();
entry.AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(55); // Expire avant le token
return token;
});
}
}
Conclusion
L'implémentation d'OAuth pour l'authentification mobile avec .NET nécessite une attention particulière à la sécurité et aux performances. Les outils natifs de l'écosystème .NET facilitent grandement cette mise en œuvre, mais il est crucial de suivre les bonnes pratiques et de maintenir une veille sur les évolutions du protocole.
Pour aller plus loin, explorez les fonctionnalités avancées comme le PKCE (Proof Key for Code Exchange) et l'authentification à deux facteurs, qui ajoutent des couches de sécurité supplémentaires à votre application.