Configures OIDC with explicit endpoints
All checks were successful
Build, Push and Run Container / build (push) Successful in 24s

Configures the OpenID Connect (OIDC) authentication flow by
explicitly setting the authorization, token, JWKS, end session, and
user info endpoints.

This change removes the custom OIDC configuration manager and
directly sets the configuration within the OIDC options. This approach
simplifies the configuration and ensures that the application uses
the correct endpoints for authentication and authorization with the
third-party provider.
This commit is contained in:
2025-08-16 23:31:35 +02:00
parent 96dd0ff99a
commit a192f1380b
2 changed files with 18 additions and 54 deletions

View File

@@ -40,13 +40,19 @@ builder.Services
.AddCookie()
.AddOpenIdConnect(o =>
{
// === Use Fleet-Auth third-party OIDC config ===
// Issuer in that doc: https://fleet-auth.tesla.com/oauth2/v3/nts
o.Authority = "https://fleet-auth.tesla.com/oauth2/v3/nts";
// Point directly at the third-party metadata you found:
// Point directly at the third-party metadata
o.MetadataAddress = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/thirdparty/.well-known/openid-configuration";
// === Use Fleet-Auth third-party OIDC config ===
o.Authority = "https://fleet-auth.tesla.com/oauth2/v3/nts";
o.Configuration ??= new OpenIdConnectConfiguration();
o.Configuration.AuthorizationEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
o.Configuration.TokenEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
o.Configuration.JwksUri = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/discovery/thirdparty/keys";
o.Configuration.EndSessionEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/logout";
o.Configuration.UserInfoEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/userinfo";
// Standard OIDC web app settings
o.ResponseType = OpenIdConnectResponseType.Code;
o.UsePkce = true;

View File

@@ -11,13 +11,12 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
public sealed class TeslaOIDCConfigurationManager : IConfigurationManager<OpenIdConnectConfiguration>
{
private readonly Func<string, string> urlOverride;
private readonly IConfigurationManager<OpenIdConnectConfiguration> _inner;
private readonly string _tokenEndpointOverride;
// No HttpClient/ServiceProvider needed — uses default retriever internally
public TeslaOIDCConfigurationManager(string metadataAddress, string tokenEndpointOverride)
public TeslaOIDCConfigurationManager(string metadataAddress, Func<string, string> urlOverride)
{
_tokenEndpointOverride = tokenEndpointOverride;
this.urlOverride = urlOverride;
_inner = new ConfigurationManager<OpenIdConnectConfiguration>(
metadataAddress,
new OpenIdConnectConfigurationRetriever());
@@ -25,51 +24,10 @@ public sealed class TeslaOIDCConfigurationManager : IConfigurationManager<OpenId
public async Task<OpenIdConnectConfiguration> GetConfigurationAsync(CancellationToken cancel)
{
var cfg = await _inner.GetConfigurationAsync(cancel).ConfigureAwait(false);
cfg.TokenEndpoint = _tokenEndpointOverride; // <-- required by Tesla
return cfg;
OpenIdConnectConfiguration? configuration = await _inner.GetConfigurationAsync(cancel);
configuration.TokenEndpoint = urlOverride(configuration.TokenEndpoint);
return configuration;
}
public void RequestRefresh() => _inner.RequestRefresh();
public sealed class SigningKeyResolver
{
private readonly HttpClient backChannel;
private readonly TimeSpan cacheDuration;
private readonly ConcurrentDictionary<string, (DateTimeOffset exp, SecurityKey[] keys)> cache;
public SigningKeyResolver(HttpClient backChannel, TimeSpan cacheDuration)
{
this.backChannel = backChannel;
this.cacheDuration = cacheDuration;
this.cache = new ConcurrentDictionary<string,(DateTimeOffset exp, SecurityKey[] keys)>();
}
public IEnumerable<SecurityKey> Resolve(string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters)
{
JwtSecurityToken jwt = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(token);
string? issuer = jwt.Issuer?.TrimEnd('/');
// Issuer is empty
if (string.IsNullOrEmpty(issuer))
return Array.Empty<SecurityKey>();
// Serve from cache if fresh
if (cache.TryGetValue(issuer, out var entry) && entry.exp > DateTimeOffset.UtcNow)
return entry.keys;
// Fetch JWKS from the same issuer (sync-over-async kept local to this callback)
string jwksUrl = $"{issuer}/certs";
string json = backChannel.GetStringAsync(jwksUrl).GetAwaiter().GetResult();
// Get result
JsonWebKeySet jwks = new JsonWebKeySet(json);
SecurityKey[] keys = jwks.Keys.Select(k => (SecurityKey)k).ToArray();
// Cache
cache[issuer] = (DateTimeOffset.UtcNow.Add(this.cacheDuration), keys);
return keys;
}
}
}