104 lines
3.7 KiB
C#
104 lines
3.7 KiB
C#
using DysonNetwork.Sphere.Auth.Proto;
|
|
using Grpc.Core;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using DysonNetwork.Sphere.Account;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NodaTime;
|
|
using System.Text.Json;
|
|
|
|
namespace DysonNetwork.Sphere.Auth;
|
|
|
|
public class AuthGrpcService : DysonNetwork.Sphere.Auth.Proto.AuthService.AuthServiceBase
|
|
{
|
|
private readonly AppDatabase _db;
|
|
private readonly AccountService _accounts;
|
|
private readonly AuthService _auth;
|
|
|
|
public AuthGrpcService(AppDatabase db, AccountService accounts, AuthService auth)
|
|
{
|
|
_db = db;
|
|
_accounts = accounts;
|
|
_auth = auth;
|
|
}
|
|
|
|
public override async Task<LoginResponse> Login(LoginRequest request, ServerCallContext context)
|
|
{
|
|
var account = await _accounts.LookupAccount(request.Username);
|
|
if (account == null)
|
|
{
|
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found."));
|
|
}
|
|
|
|
var factor = await _db.AccountAuthFactors.FirstOrDefaultAsync(f => f.AccountId == account.Id && f.Type == AccountAuthFactorType.Password);
|
|
if (factor == null || !factor.VerifyPassword(request.Password))
|
|
{
|
|
throw new RpcException(new Grpc.Core.Status(StatusCode.Unauthenticated, "Invalid credentials."));
|
|
}
|
|
|
|
var session = new Session
|
|
{
|
|
LastGrantedAt = Instant.FromDateTimeUtc(DateTime.UtcNow),
|
|
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddDays(30)),
|
|
Account = account,
|
|
Challenge = new Challenge() // Create a dummy challenge
|
|
};
|
|
|
|
_db.AuthSessions.Add(session);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var token = _auth.CreateToken(session);
|
|
|
|
return new LoginResponse
|
|
{
|
|
AccessToken = token,
|
|
ExpiresIn = (long)(session.ExpiredAt.Value - session.LastGrantedAt.Value).TotalSeconds
|
|
};
|
|
}
|
|
|
|
public override async Task<IntrospectionResponse> IntrospectToken(IntrospectTokenRequest request, ServerCallContext context)
|
|
{
|
|
if (_auth.ValidateToken(request.Token, out var sessionId))
|
|
{
|
|
var session = await _db.AuthSessions
|
|
.Include(s => s.Account)
|
|
.Include(s => s.Challenge)
|
|
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
|
|
|
if (session != null)
|
|
{
|
|
return new IntrospectionResponse
|
|
{
|
|
Active = true,
|
|
Claims = JsonSerializer.Serialize(new { sub = session.AccountId }),
|
|
ClientId = session.AppId?.ToString() ?? "",
|
|
Username = session.Account.Name,
|
|
Scope = string.Join(" ", session.Challenge.Scopes),
|
|
Iat = Timestamp.FromDateTime(session.CreatedAt.ToDateTimeUtc()),
|
|
Exp = Timestamp.FromDateTime(session.ExpiredAt?.ToDateTimeUtc() ?? DateTime.MaxValue)
|
|
};
|
|
}
|
|
}
|
|
|
|
return new IntrospectionResponse { Active = false };
|
|
}
|
|
|
|
public override async Task<Empty> Logout(Empty request, ServerCallContext context)
|
|
{
|
|
var authorizationHeader = context.RequestHeaders.FirstOrDefault(h => h.Key == "authorization");
|
|
if (authorizationHeader != null)
|
|
{
|
|
var token = authorizationHeader.Value.Replace("Bearer ", "");
|
|
if (_auth.ValidateToken(token, out var sessionId))
|
|
{
|
|
var session = await _db.AuthSessions.FindAsync(sessionId);
|
|
if (session != null)
|
|
{
|
|
_db.AuthSessions.Remove(session);
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
}
|
|
}
|
|
return new Empty();
|
|
}
|
|
}
|