Files
Swarm/DysonNetwork.Sphere/Auth/AuthGrpcService.cs

95 lines
3.5 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;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Sphere.Auth;
public class AuthGrpcService(AppDatabase db, AccountService accounts, AuthService auth)
: DysonNetwork.Sphere.Auth.Proto.AuthService.AuthServiceBase
{
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();
}
}