sec: return the token instead of performing actions with the user
This commit is contained in:
@ -10,7 +10,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
@ -19,7 +18,8 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Mirea.Api.Security.Services;
|
||||
|
||||
public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider, (string ClientId, string Secret)> providers, string secretKey)
|
||||
public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider, (string ClientId, string Secret)> providers,
|
||||
ICacheService cache)
|
||||
{
|
||||
public required ReadOnlyMemory<byte> SecretKey { private get; init; }
|
||||
|
||||
@ -195,21 +195,26 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
|
||||
public (OAuthProvider Provider, Uri Redirect)[] GetAvailableProviders(string redirectUri) =>
|
||||
[.. providers.Select(x => (x.Key, new Uri(redirectUri.TrimEnd('/') + "/?provider=" + (int)x.Key)))];
|
||||
|
||||
public async Task<(OAuthProvider provider, OAuthUser User)> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions,
|
||||
public async Task<LoginOAuthResult> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions,
|
||||
string redirectUrl, string code, string state, CancellationToken cancellation = default)
|
||||
{
|
||||
|
||||
var result = new LoginOAuthResult()
|
||||
{
|
||||
Token = GeneratorKey.GenerateBase64(32)
|
||||
};
|
||||
var parts = state.Split('_');
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new SecurityException("The request data is invalid or malformed.");
|
||||
|
||||
result.ErrorMessage = "The request data is invalid or malformed.";
|
||||
return result;
|
||||
}
|
||||
|
||||
var payload = DecryptPayload(parts[0]);
|
||||
var checksum = parts[1];
|
||||
|
||||
result.Callback = new Uri(payload.Callback);
|
||||
|
||||
if (!providers.TryGetValue(payload.Provider, out var providerInfo) ||
|
||||
!ProviderData.TryGetValue(payload.Provider, out var currentProviderStruct))
|
||||
{
|
||||
@ -217,12 +222,15 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
|
||||
"is not registered as a possible data recipient from state: {State}",
|
||||
state);
|
||||
|
||||
throw new SecurityException("Invalid authorization request. Please try again later.");
|
||||
result.ErrorMessage = "Invalid authorization request. Please try again later.";
|
||||
return result;
|
||||
}
|
||||
|
||||
var requestInfo = new RequestContextInfo(context, cookieOptions);
|
||||
var checksumRequest = GetHmacString(requestInfo);
|
||||
|
||||
|
||||
result.ErrorMessage = "Authorization failed. Please try again later.";
|
||||
|
||||
if (checksumRequest != checksum)
|
||||
{
|
||||
logger.LogWarning(
|
||||
@ -231,10 +239,11 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
|
||||
checksumRequest,
|
||||
checksum
|
||||
);
|
||||
throw new SecurityException("Suspicious activity detected. Please try again.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OAuthTokenResponse? accessToken = null;
|
||||
OAuthTokenResponse? accessToken;
|
||||
try
|
||||
{
|
||||
accessToken = await ExchangeCodeForTokensAsync(currentProviderStruct.TokenUrl, redirectUrl, code, providerInfo.ClientId,
|
||||
@ -243,27 +252,41 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to exchange code for access token with provider {Provider}. State: {State}",
|
||||
provider,
|
||||
secretStateData);
|
||||
payload.Provider,
|
||||
checksum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (accessToken == null)
|
||||
throw new SecurityException("Unable to complete authorization with the provider. Please try again later.");
|
||||
return result;
|
||||
|
||||
OAuthUser? result = null;
|
||||
OAuthUser? user;
|
||||
try
|
||||
{
|
||||
result = await GetUserProfileAsync(currentProviderStruct.UserInfoUrl, currentProviderStruct.AuthHeader, accessToken.AccessToken,
|
||||
provider, cancellation);
|
||||
user = await GetUserProfileAsync(currentProviderStruct.UserInfoUrl, currentProviderStruct.AuthHeader, accessToken.AccessToken,
|
||||
payload.Provider, cancellation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to retrieve user information from provider {Provider}", provider);
|
||||
logger.LogWarning(ex, "Failed to retrieve user information from provider {Provider}",
|
||||
payload.Provider);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
throw new SecurityException("Unable to retrieve user information. Please check the details and try again.");
|
||||
if (user == null)
|
||||
return result;
|
||||
|
||||
return (provider, result);
|
||||
result.ErrorMessage = null;
|
||||
result.Success = true;
|
||||
|
||||
await cache.SetAsync(
|
||||
result.Token,
|
||||
JsonSerializer.SerializeToUtf8Bytes(user),
|
||||
absoluteExpirationRelativeToNow: TimeSpan.FromMinutes(15),
|
||||
cancellationToken: cancellation);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user