From e1f1106c8d692c619f94f840d136f7b4ab51e855 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 30 Apr 2024 00:22:25 +0200 Subject: [PATCH] Add authmethod config value for oidc providers (#453) --- .env.example | 4 ++++ .../AuthenticationModule.cs | 15 ++++++++++++++ .../Controllers/OidcController.cs | 20 +++++++++++++------ .../Models/Options/PermissionOption.cs | 14 +++++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index e947507ea..ed12ec455 100644 --- a/.env.example +++ b/.env.example @@ -55,6 +55,10 @@ OIDC_SERVICE_AUTHORIZATION=https://url-of-the-authorization-endpoint-of-the-oidc OIDC_SERVICE_TOKEN=https://url-of-the-token-endpoint-of-the-oidc-service.com/token OIDC_SERVICE_PROFILE=https://url-of-the-profile-endpoint-of-the-oidc-service.com/userinfo OIDC_SERVICE_SCOPE="the list of scopes space separeted like email identity" +# Token authentication method as seen in https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication +# Supported values: ClientSecretBasic (default) or ClientSecretPost +# If in doupt, leave this empty. +OIDC_SERVICE_AUTHMETHOD=ClientSecretBasic # on the previous list, service is the internal name of your service, you can add as many as you want. diff --git a/back/src/Kyoo.Authentication/AuthenticationModule.cs b/back/src/Kyoo.Authentication/AuthenticationModule.cs index 36526676f..611a9eed9 100644 --- a/back/src/Kyoo.Authentication/AuthenticationModule.cs +++ b/back/src/Kyoo.Authentication/AuthenticationModule.cs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -100,6 +101,20 @@ public static void ConfigureAuthentication(this WebApplicationBuilder builder) case "logo": acc[provider].LogoUrl = val.Value; break; + case "clientauthmethod": + case "authmethod": + case "auth": + case "method": + if (!Enum.TryParse(val.Value, out AuthMethod method)) + { + Log.Error( + "Invalid AuthMethod value: {AuthMethod}. Ignoring.", + val.Value + ); + break; + } + acc[provider].ClientAuthMethod = method; + break; default: Log.Error("Invalid oidc config value: {Key}", key); return acc; diff --git a/back/src/Kyoo.Authentication/Controllers/OidcController.cs b/back/src/Kyoo.Authentication/Controllers/OidcController.cs index d59092565..b4fbbee91 100644 --- a/back/src/Kyoo.Authentication/Controllers/OidcController.cs +++ b/back/src/Kyoo.Authentication/Controllers/OidcController.cs @@ -43,19 +43,27 @@ PermissionOption options HttpClient client = clientFactory.CreateClient(); - string auth = Convert.ToBase64String( - Encoding.UTF8.GetBytes($"{prov.ClientId}:{prov.Secret}") - ); - client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}"); Dictionary data = new() { ["code"] = code, - ["client_id"] = prov.ClientId, - ["client_secret"] = prov.Secret, ["redirect_uri"] = $"{options.PublicUrl.TrimEnd('/')}/api/auth/logged/{provider}", ["grant_type"] = "authorization_code", }; + + if (prov.ClientAuthMethod == AuthMethod.ClientSecretBasic) + { + string auth = Convert.ToBase64String( + Encoding.UTF8.GetBytes($"{prov.ClientId}:{prov.Secret}") + ); + client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}"); + } + else if (prov.ClientAuthMethod == AuthMethod.ClientSecretPost) + { + data["client_id"] = prov.ClientId; + data["client_secret"] = prov.Secret; + } + HttpResponseMessage resp = prov.TokenUseJsonBody ? await client.PostAsJsonAsync(prov.TokenUrl, data) : await client.PostAsync(prov.TokenUrl, new FormUrlEncodedContent(data)); diff --git a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs b/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs index 90b8cb677..458003e30 100644 --- a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs +++ b/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs @@ -67,6 +67,13 @@ public class PermissionOption public Dictionary OIDC { get; set; } } +public enum AuthMethod +{ + ClientSecretBasic, + ClientSecretPost, + None, +} + public class OidcProvider { public string DisplayName { get; set; } @@ -79,6 +86,11 @@ public class OidcProvider /// public bool TokenUseJsonBody { get; set; } + /// + /// The OIDC spec allows multiples ways of authorizing the client. + /// + public AuthMethod ClientAuthMethod { get; set; } = AuthMethod.ClientSecretBasic; + public string ProfileUrl { get; set; } public string? Scope { get; set; } public string ClientId { get; set; } @@ -108,6 +120,7 @@ public OidcProvider(string provider) ClientId = KnownProviders[provider].ClientId; Secret = KnownProviders[provider].Secret; TokenUseJsonBody = KnownProviders[provider].TokenUseJsonBody; + ClientAuthMethod = KnownProviders[provider].ClientAuthMethod; GetProfileUrl = KnownProviders[provider].GetProfileUrl; GetExtraHeaders = KnownProviders[provider].GetExtraHeaders; } @@ -144,6 +157,7 @@ public OidcProvider(string provider) // does not seems to have scopes Scope = null, TokenUseJsonBody = true, + ClientAuthMethod = AuthMethod.ClientSecretPost, GetProfileUrl = (profile) => $"https://simkl.com/{profile.Sub}/dashboard/", GetExtraHeaders = (OidcProvider self) => new() { ["simkl-api-key"] = self.ClientId },