Release v2.1.3 #49
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Options;
|
|||||||
namespace HopFrame.Api.Controller;
|
namespace HopFrame.Api.Controller;
|
||||||
|
|
||||||
[ApiController, Route("api/v1/openid")]
|
[ApiController, Route("api/v1/openid")]
|
||||||
public class OpenIdController(IOpenIdAccessor accessor, IOptions<OpenIdOptions> options) : ControllerBase {
|
public class OpenIdController(IOpenIdAccessor accessor) : ControllerBase {
|
||||||
public const string DefaultCallback = "api/v1/openid/callback";
|
public const string DefaultCallback = "api/v1/openid/callback";
|
||||||
|
|
||||||
[HttpGet("redirect")]
|
[HttpGet("redirect")]
|
||||||
@@ -35,16 +35,7 @@ public class OpenIdController(IOpenIdAccessor accessor, IOptions<OpenIdOptions>
|
|||||||
return Forbid("Authorization code is not valid");
|
return Forbid("Authorization code is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Cookies.Append(ITokenContext.AccessTokenType, token.AccessToken, new CookieOptions {
|
accessor.SetAuthenticationCookies(token);
|
||||||
MaxAge = TimeSpan.FromSeconds(token.ExpiresIn),
|
|
||||||
HttpOnly = false,
|
|
||||||
Secure = true
|
|
||||||
});
|
|
||||||
Response.Cookies.Append(ITokenContext.RefreshTokenType, token.RefreshToken, new CookieOptions {
|
|
||||||
MaxAge = options.Value.RefreshToken.ConstructTimeSpan,
|
|
||||||
HttpOnly = false,
|
|
||||||
Secure = true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(state)) {
|
if (string.IsNullOrEmpty(state)) {
|
||||||
return Ok(new SingleValueResult<string>(token.AccessToken));
|
return Ok(new SingleValueResult<string>(token.AccessToken));
|
||||||
@@ -65,11 +56,7 @@ public class OpenIdController(IOpenIdAccessor accessor, IOptions<OpenIdOptions>
|
|||||||
if (token is null)
|
if (token is null)
|
||||||
return NotFound("Refresh token not valid");
|
return NotFound("Refresh token not valid");
|
||||||
|
|
||||||
Response.Cookies.Append(ITokenContext.AccessTokenType, token.AccessToken, new CookieOptions {
|
accessor.SetAuthenticationCookies(token);
|
||||||
MaxAge = TimeSpan.FromSeconds(token.ExpiresIn),
|
|
||||||
HttpOnly = false,
|
|
||||||
Secure = true
|
|
||||||
});
|
|
||||||
|
|
||||||
return Ok(new SingleValueResult<string>(token.AccessToken));
|
return Ok(new SingleValueResult<string>(token.AccessToken));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ public interface IOpenIdAccessor {
|
|||||||
Task<string> ConstructAuthUri(string state = null);
|
Task<string> ConstructAuthUri(string state = null);
|
||||||
Task<OpenIdIntrospection> InspectToken(string token);
|
Task<OpenIdIntrospection> InspectToken(string token);
|
||||||
Task<OpenIdToken> RefreshAccessToken(string refreshToken);
|
Task<OpenIdToken> RefreshAccessToken(string refreshToken);
|
||||||
|
void SetAuthenticationCookies(OpenIdToken token);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using HopFrame.Security.Authentication.OpenID.Models;
|
using HopFrame.Security.Authentication.OpenID.Models;
|
||||||
using HopFrame.Security.Authentication.OpenID.Options;
|
using HopFrame.Security.Authentication.OpenID.Options;
|
||||||
|
using HopFrame.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -18,7 +19,7 @@ internal class OpenIdAccessor(IHttpClientFactory clientFactory, IOptions<OpenIdO
|
|||||||
}
|
}
|
||||||
|
|
||||||
var client = clientFactory.CreateClient();
|
var client = clientFactory.CreateClient();
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, Path.Combine(options.Value.Issuer, ".well-known/openid-configuration"));
|
var request = new HttpRequestMessage(HttpMethod.Get, Path.Combine(options.Value.Issuer, ".well-known/openid-configuration").Replace("\\", "/"));
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
@@ -38,7 +39,7 @@ internal class OpenIdAccessor(IHttpClientFactory clientFactory, IOptions<OpenIdO
|
|||||||
}
|
}
|
||||||
|
|
||||||
var protocol = accessor.HttpContext!.Request.IsHttps ? "https" : "http";
|
var protocol = accessor.HttpContext!.Request.IsHttps ? "https" : "http";
|
||||||
var callback = options.Value.Callback ?? Path.Combine($"{protocol}://{accessor.HttpContext!.Request.Host.Value}", IOpenIdAccessor.DefaultCallback);
|
var callback = options.Value.Callback ?? Path.Combine($"{protocol}://{accessor.HttpContext!.Request.Host.Value}", IOpenIdAccessor.DefaultCallback).Replace("\\", "/");
|
||||||
|
|
||||||
var configuration = await LoadConfiguration();
|
var configuration = await LoadConfiguration();
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ internal class OpenIdAccessor(IHttpClientFactory clientFactory, IOptions<OpenIdO
|
|||||||
|
|
||||||
public async Task<string> ConstructAuthUri(string state = null) {
|
public async Task<string> ConstructAuthUri(string state = null) {
|
||||||
var protocol = accessor.HttpContext!.Request.IsHttps ? "https" : "http";
|
var protocol = accessor.HttpContext!.Request.IsHttps ? "https" : "http";
|
||||||
var callback = options.Value.Callback ?? Path.Combine($"{protocol}://{accessor.HttpContext!.Request.Host.Value}", IOpenIdAccessor.DefaultCallback);
|
var callback = options.Value.Callback ?? Path.Combine($"{protocol}://{accessor.HttpContext!.Request.Host.Value}", IOpenIdAccessor.DefaultCallback).Replace("\\", "/");
|
||||||
|
|
||||||
var configuration = await LoadConfiguration();
|
var configuration = await LoadConfiguration();
|
||||||
return $"{configuration.AuthorizationEndpoint}?response_type=code&client_id={options.Value.ClientId}&redirect_uri={callback}&scope=openid%20profile%20email%20offline_access&state={state}";
|
return $"{configuration.AuthorizationEndpoint}?response_type=code&client_id={options.Value.ClientId}&redirect_uri={callback}&scope=openid%20profile%20email%20offline_access&state={state}";
|
||||||
@@ -120,4 +121,20 @@ internal class OpenIdAccessor(IHttpClientFactory clientFactory, IOptions<OpenIdO
|
|||||||
|
|
||||||
return await JsonSerializer.DeserializeAsync<OpenIdToken>(await response.Content.ReadAsStreamAsync());
|
return await JsonSerializer.DeserializeAsync<OpenIdToken>(await response.Content.ReadAsStreamAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAuthenticationCookies(OpenIdToken token) {
|
||||||
|
if (token.AccessToken is not null)
|
||||||
|
accessor.HttpContext!.Response.Cookies.Append(ITokenContext.AccessTokenType, token.AccessToken, new CookieOptions {
|
||||||
|
MaxAge = TimeSpan.FromSeconds(token.ExpiresIn),
|
||||||
|
HttpOnly = false,
|
||||||
|
Secure = true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (token.RefreshToken is not null)
|
||||||
|
accessor.HttpContext!.Response.Cookies.Append(ITokenContext.RefreshTokenType, token.RefreshToken, new CookieOptions {
|
||||||
|
MaxAge = options.Value.RefreshToken.ConstructTimeSpan,
|
||||||
|
HttpOnly = false,
|
||||||
|
Secure = true
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -108,11 +108,7 @@ internal class AuthService(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, openIdToken.AccessToken, new CookieOptions {
|
accessor.SetAuthenticationCookies(openIdToken);
|
||||||
MaxAge = TimeSpan.FromSeconds(openIdToken.ExpiresIn),
|
|
||||||
HttpOnly = false,
|
|
||||||
Secure = true
|
|
||||||
});
|
|
||||||
return new() {
|
return new() {
|
||||||
Owner = user,
|
Owner = user,
|
||||||
CreatedAt = DateTime.Now,
|
CreatedAt = DateTime.Now,
|
||||||
|
|||||||
Reference in New Issue
Block a user