You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Ombi/Ombi/Ombi/Auth/TokenProviderMiddleware.cs

152 lines
5.2 KiB

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Ombi.Core.IdentityResolver;
using Ombi.Models;
using Ombi.Store.Context;
using Ombi.Store.Repository;
namespace Ombi.Auth
{
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly JsonSerializerSettings _serializerSettings;
private readonly IUserIdentityManager _identityManager;
public TokenProviderMiddleware(
RequestDelegate next,
IOptions<TokenProviderOptions> options, IUserIdentityManager manager)
{
_next = next;
_options = options.Value;
ThrowIfInvalidOptions(_options);
_serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
_identityManager = manager;
}
public Task Invoke(HttpContext context)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
return _next(context);
}
// Request must be POST with Content-Type: application/json
if (!context.Request.Method.Equals("POST")
)
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context);
}
private async Task GenerateToken(HttpContext context)
{
var request = context.Request;
UserAuthModel userInfo;
using (var bodyReader = new StreamReader(request.Body))
{
string body = await bodyReader.ReadToEndAsync();
userInfo = JsonConvert.DeserializeObject<UserAuthModel>(body);
}
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password, _identityManager);
if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid username or password.");
return;
}
var now = DateTime.UtcNow;
// Specifically add the jti (nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var jwtClaims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, userInfo.Username),
new Claim(JwtRegisteredClaimNames.Jti, await _options.NonceGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(now).ToUniversalTime().ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
identity.Claims.ToList().AddRange(jwtClaims);
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: identity.Claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)_options.Expiration.TotalSeconds
};
// Serialize and return the response
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, _serializerSettings));
}
private static void ThrowIfInvalidOptions(TokenProviderOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Path));
}
if (string.IsNullOrEmpty(options.Issuer))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Issuer));
}
if (string.IsNullOrEmpty(options.Audience))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Audience));
}
if (options.Expiration == TimeSpan.Zero)
{
throw new ArgumentException("Must be a non-zero TimeSpan.", nameof(TokenProviderOptions.Expiration));
}
if (options.IdentityResolver == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.IdentityResolver));
}
if (options.SigningCredentials == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.SigningCredentials));
}
if (options.NonceGenerator == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.NonceGenerator));
}
}
}
}