Skip to content

Commit 3e4140d

Browse files
committed
Added new configuration params
- A new configuration param for a custom root CA certificate - A new configuration param to enable/disable user creation on login - Fixed build for jellyfin 10.8.0
1 parent 1e80db8 commit 3e4140d

7 files changed

+248
-104
lines changed

.prettierrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"singleAttributePerLine": true
3+
}

Authelia-Auth/Authelia-Auth.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
55
<RootNamespace>Jellyfin.Plugin.Authelia_Auth</RootNamespace>
6-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
7-
<FileVersion>1.0.0.0</FileVersion>
6+
<AssemblyVersion>1.0.12.0</AssemblyVersion>
7+
<FileVersion>1.0.12.0</FileVersion>
88
<GenerateDocumentationFile>true</GenerateDocumentationFile>
99
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1010
<ProduceReferenceAssemblyInOutDir>true</ProduceReferenceAssemblyInOutDir>
@@ -17,7 +17,7 @@
1717
</ItemGroup>
1818

1919
<ItemGroup>
20-
<PackageReference Include="Jellyfin.Controller" Version="10.*-*" />
20+
<PackageReference Include="Jellyfin.Controller" Version="10.8.0" />
2121
</ItemGroup>
2222

2323
<!-- Code Analyzers-->

Authelia-Auth/AutheliaAuthenticationProviderPlugin.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
2+
using System.Security.Cryptography;
23
using System.Threading.Tasks;
34
using Jellyfin.Data.Entities;
45
using MediaBrowser.Common;
56
using MediaBrowser.Controller.Authentication;
67
using MediaBrowser.Controller.Library;
8+
using MediaBrowser.Model.Cryptography;
79
using Microsoft.Extensions.Logging;
810

911
namespace Jellyfin.Plugin.Authelia_Auth
@@ -13,18 +15,21 @@ namespace Jellyfin.Plugin.Authelia_Auth
1315
/// </summary>
1416
public class AutheliaAuthenticationProviderPlugin : IAuthenticationProvider
1517
{
16-
private readonly ILogger<AutheliaAuthenticationProviderPlugin> _logger;
1718
private readonly IApplicationHost _applicationHost;
19+
private readonly ILogger<AutheliaAuthenticationProviderPlugin> _logger;
20+
private readonly ICryptoProvider _cryptoProvider;
1821

1922
/// <summary>
2023
/// Initializes a new instance of the <see cref="AutheliaAuthenticationProviderPlugin"/> class.
2124
/// </summary>
2225
/// <param name="applicationHost">Instance of the <see cref="IApplicationHost"/> interface.</param>
2326
/// <param name="logger">Instance of the <see cref="ILogger{AutheliaAuthenticationProviderPlugin}"/> interface.</param>
24-
public AutheliaAuthenticationProviderPlugin(IApplicationHost applicationHost, ILogger<AutheliaAuthenticationProviderPlugin> logger)
27+
/// <param name="cryptoProvider">Instance of the <see cref="ILogger{ICryptoProvider}"/> interface.</param>
28+
public AutheliaAuthenticationProviderPlugin(IApplicationHost applicationHost, ILogger<AutheliaAuthenticationProviderPlugin> logger, ICryptoProvider cryptoProvider)
2529
{
2630
_logger = logger;
2731
_applicationHost = applicationHost;
32+
_cryptoProvider = cryptoProvider;
2833
}
2934

3035
/// <summary>
@@ -48,18 +53,29 @@ public async Task<ProviderAuthenticationResult> Authenticate(string username, st
4853
{
4954
var userManager = _applicationHost.Resolve<IUserManager>();
5055
var config = AutheliaPlugin.Instance.Configuration;
56+
5157
var auth = await new Authenticator().Authenticate(config, username, password);
5258

59+
User user = null;
5360
try
5461
{
55-
userManager.GetUserByName(username);
62+
user = userManager.GetUserByName(username);
5663
}
5764
catch (Exception e)
5865
{
5966
_logger.LogError("User Manager could not find a user for Authelia User", e);
6067
throw new AuthenticationException("Error completing Authelia login. Invalid username or password.");
6168
}
6269

70+
if (config.CreateUserIfNotExists && user == null)
71+
{
72+
_logger.LogInformation("Authelia user doesn't exist, creating...");
73+
user = await userManager.CreateUserAsync(username).ConfigureAwait(false);
74+
75+
user.AuthenticationProviderId = GetType().FullName;
76+
user.Password = _cryptoProvider.CreatePasswordHash(Convert.ToBase64String(RandomNumberGenerator.GetBytes(64))).ToString();
77+
}
78+
6379
return auth;
6480
}
6581

Authelia-Auth/Authenticator.cs

+46-43
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
using System;
2-
using System.Linq;
32
using System.Net;
43
using System.Net.Http;
54
using System.Net.Http.Json;
5+
using System.Security.Cryptography.X509Certificates;
66
using System.Text;
77
using System.Text.Json.Nodes;
88
using System.Text.Json.Serialization;
9-
using System.Text.RegularExpressions;
109
using System.Threading.Tasks;
1110
using Jellyfin.Plugin.Authelia_Auth.Config;
1211
using MediaBrowser.Controller.Authentication;
@@ -46,8 +45,6 @@ public class UserInfoResponse
4645
/// </summary>
4746
public class Authenticator
4847
{
49-
private Regex cookieRe = new Regex("authelia_session=([^;]+);");
50-
5148
/// <summary>
5249
/// Authenticate user.
5350
/// </summary>
@@ -59,59 +56,65 @@ public class Authenticator
5956
public async Task<ProviderAuthenticationResult> Authenticate(PluginConfiguration config, string username, string password)
6057
{
6158
var cookieContainer = new CookieContainer();
62-
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
63-
using (var client = new HttpClient(handler) { BaseAddress = new Uri(config.AutheliaServer) })
59+
using var handler = new HttpClientHandler()
6460
{
65-
var jsonBody = new JsonObject();
66-
jsonBody.Add("username", username);
67-
jsonBody.Add("password", password);
68-
jsonBody.Add("targetURL", config.JellyfinUrl);
69-
jsonBody.Add("requestMethod", "GET");
70-
jsonBody.Add("keepMeLoggedIn", true);
71-
72-
var session = string.Empty;
73-
74-
using (var content = new StringContent(jsonBody.ToString(), Encoding.UTF8, "application/json"))
61+
CookieContainer = cookieContainer,
62+
ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
7563
{
76-
var response = await client.PostAsync("/api/firstfactor", content);
77-
78-
if (!response.IsSuccessStatusCode)
64+
if (!string.IsNullOrWhiteSpace(config.AutheliaRootCa))
7965
{
80-
throw new AuthenticationException("Invalid username or password.");
66+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
67+
chain.ChainPolicy.CustomTrustStore.ImportFromPem(config.AutheliaRootCa);
8168
}
8269

83-
var setCookie = response.Headers.GetValues("Set-Cookie").FirstOrDefault(string.Empty);
84-
session = cookieRe.Match(setCookie).Groups[1].Value;
70+
return chain.Build(cert);
8571
}
72+
};
73+
using var client = new HttpClient(handler) { BaseAddress = new Uri(config.AutheliaServer) };
8674

87-
// Allow using internal authelia url instead of proxied
88-
cookieContainer.Add(new Uri(config.AutheliaServer), new Cookie("authelia_session", session));
75+
var jsonBody = new JsonObject
76+
{
77+
{ "username", username },
78+
{ "password", password },
79+
{ "targetURL", config.JellyfinUrl },
80+
{ "requestMethod", "GET" },
81+
{ "keepMeLoggedIn", true }
82+
};
8983

90-
using (var request = new HttpRequestMessage(HttpMethod.Get, "/api/verify"))
84+
using (var content = new StringContent(jsonBody.ToString(), Encoding.UTF8, "application/json"))
85+
{
86+
var response = await client.PostAsync("/api/firstfactor", content);
87+
88+
if (!response.IsSuccessStatusCode)
9189
{
92-
request.Headers.Add("X-Original-Url", config.JellyfinUrl);
93-
request.Headers.Add("X-Forwarded-Method", "GET");
94-
var accessResponse = await client.SendAsync(request);
95-
if (!accessResponse.IsSuccessStatusCode)
96-
{
97-
throw new AuthenticationException("User doesn't have access to this service.");
98-
}
90+
throw new AuthenticationException("Invalid username or password.");
9991
}
92+
}
10093

101-
try
94+
using (var request = new HttpRequestMessage(HttpMethod.Get, "/api/verify"))
95+
{
96+
request.Headers.Add("X-Original-Url", config.JellyfinUrl);
97+
request.Headers.Add("X-Forwarded-Method", "GET");
98+
var accessResponse = await client.SendAsync(request);
99+
if (!accessResponse.IsSuccessStatusCode)
102100
{
103-
var userInfoResponse = await client.GetFromJsonAsync<UserInfoResponse>("/api/user/info");
104-
105-
return new ProviderAuthenticationResult
106-
{
107-
Username = username,
108-
DisplayName = userInfoResponse.Data.DisplayName,
109-
};
101+
throw new AuthenticationException("User doesn't have access to this service.");
110102
}
111-
catch
103+
}
104+
105+
try
106+
{
107+
var userInfoResponse = await client.GetFromJsonAsync<UserInfoResponse>("/api/user/info");
108+
109+
return new ProviderAuthenticationResult
112110
{
113-
throw new AuthenticationException("Invalid username or password.");
114-
}
111+
Username = username,
112+
DisplayName = userInfoResponse.Data.DisplayName,
113+
};
114+
}
115+
catch
116+
{
117+
throw new AuthenticationException("Invalid username or password.");
115118
}
116119
}
117120
}

Authelia-Auth/Config/PluginConfiguration.cs

+12
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@ public PluginConfiguration()
1414
{
1515
AutheliaServer = "http://authelia";
1616
JellyfinUrl = "http://jellyfin";
17+
AutheliaRootCa = string.Empty;
18+
CreateUserIfNotExists = true;
1719
}
1820

1921
/// <summary>
2022
/// Gets or sets the Authelia server address.
2123
/// </summary>
2224
public string AutheliaServer { get; set; }
2325

26+
/// <summary>
27+
/// Gets or sets the Authelia root CA certificate.
28+
/// </summary>
29+
public string AutheliaRootCa { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets a value indicating whether a user will be created on successful authentication if it does not exist in Jellyfin.
33+
/// </summary>
34+
public bool CreateUserIfNotExists { get; set; }
35+
2436
/// <summary>
2537
/// Gets or sets the jellyfin URL.
2638
/// </summary>

0 commit comments

Comments
 (0)