| 
									
										
										
										
											2024-12-09 21:11:46 +01:00
										 |  |  | // Copyright (C) 2023-present sam/u1f320 (vulpine.solutions) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published | 
					
						
							|  |  |  | // by the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <https://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | using System.Security.Cryptography; | 
					
						
							| 
									
										
										
										
											2024-06-04 17:38:59 +02:00
										 |  |  | using Foxnouns.Backend.Database.Models; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Foxnouns.Backend.Utils; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-08 19:03:04 +02:00
										 |  |  | public static class AuthUtils | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     public const string ClientCredentials = "client_credentials"; | 
					
						
							|  |  |  |     public const string AuthorizationCode = "authorization_code"; | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |     private static readonly string[] ForbiddenSchemes = | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         "javascript", | 
					
						
							|  |  |  |         "file", | 
					
						
							|  |  |  |         "data", | 
					
						
							|  |  |  |         "mailto", | 
					
						
							|  |  |  |         "tel", | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public static readonly string[] UserScopes = | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |     [ | 
					
						
							|  |  |  |         "user.read_hidden", | 
					
						
							|  |  |  |         "user.read_privileged", | 
					
						
							|  |  |  |         "user.update", | 
					
						
							| 
									
										
										
										
											2024-12-11 16:54:06 +01:00
										 |  |  |         "user.read_flags", | 
					
						
							|  |  |  |         "user.create_flags", | 
					
						
							|  |  |  |         "user.update_flags", | 
					
						
							| 
									
										
										
										
											2024-12-17 17:52:32 +01:00
										 |  |  |         "user.moderation", | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static readonly string[] MemberScopes = | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         "member.read", | 
					
						
							|  |  |  |         "member.update", | 
					
						
							|  |  |  |         "member.create", | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /// <summary> | 
					
						
							|  |  |  |     /// All scopes endpoints can be secured by. This does *not* include the catch-all token scopes. | 
					
						
							|  |  |  |     /// </summary> | 
					
						
							| 
									
										
										
										
											2024-07-12 17:12:24 +02:00
										 |  |  |     public static readonly string[] Scopes = ["identify", .. UserScopes, .. MemberScopes]; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /// <summary> | 
					
						
							|  |  |  |     /// All scopes that can be granted to applications and tokens. Includes the catch-all token scopes, | 
					
						
							|  |  |  |     /// except for "*" which is only granted to the frontend. | 
					
						
							|  |  |  |     /// </summary> | 
					
						
							| 
									
										
										
										
											2024-07-12 17:12:24 +02:00
										 |  |  |     public static readonly string[] ApplicationScopes = [.. Scopes, "user", "member"]; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public static string[] ExpandScopes(this string[] scopes) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |         if (scopes.Contains("*")) | 
					
						
							|  |  |  |             return ["*", .. Scopes]; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  |         List<string> expandedScopes = ["identify"]; | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |         if (scopes.Contains("user")) | 
					
						
							|  |  |  |             expandedScopes.AddRange(UserScopes); | 
					
						
							|  |  |  |         if (scopes.Contains("member")) | 
					
						
							|  |  |  |             expandedScopes.AddRange(MemberScopes); | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return expandedScopes.ToArray(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-12 17:12:24 +02:00
										 |  |  |     public static bool HasScope(this Token? token, string scope) => | 
					
						
							|  |  |  |         token?.Scopes.ExpandScopes().Contains(scope) == true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-04 17:38:59 +02:00
										 |  |  |     private static string[] ExpandAppScopes(this string[] scopes) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         var expandedScopes = scopes.ExpandScopes().ToList(); | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |         if (scopes.Contains("user")) | 
					
						
							|  |  |  |             expandedScopes.Add("user"); | 
					
						
							|  |  |  |         if (scopes.Contains("member")) | 
					
						
							|  |  |  |             expandedScopes.Add("member"); | 
					
						
							| 
									
										
										
										
											2024-06-04 17:38:59 +02:00
										 |  |  |         return expandedScopes.ToArray(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static bool ValidateScopes(Application application, string[] scopes) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-12-08 15:07:25 +01:00
										 |  |  |         string[] expandedScopes = scopes.ExpandScopes(); | 
					
						
							|  |  |  |         string[] appScopes = application.Scopes.ExpandAppScopes(); | 
					
						
							| 
									
										
										
										
											2024-06-04 17:38:59 +02:00
										 |  |  |         return !expandedScopes.Except(appScopes).Any(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  |     public static bool ValidateRedirectUri(string uri) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         try | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2024-12-08 15:07:25 +01:00
										 |  |  |             string scheme = new Uri(uri).Scheme; | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  |             return !ForbiddenSchemes.Contains(scheme); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         catch | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static bool TryFromBase64String(string b64, out byte[] bytes) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         try | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bytes = Convert.FromBase64String(b64); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-07-13 19:38:40 +02:00
										 |  |  |         catch | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             bytes = []; | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-09-25 19:48:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-11 16:23:45 +02:00
										 |  |  |     public static bool TryParseToken(string? input, out byte[] rawToken) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         rawToken = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  |         if (string.IsNullOrWhiteSpace(input)) | 
					
						
							|  |  |  |             return false; | 
					
						
							| 
									
										
										
										
											2024-09-11 16:23:45 +02:00
										 |  |  |         if (input.StartsWith("bearer ", StringComparison.InvariantCultureIgnoreCase)) | 
					
						
							|  |  |  |             input = input["bearer ".Length..]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return TryFromBase64String(input, out rawToken); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-14 16:39:02 +01:00
										 |  |  |     public static string RandomUrlUnsafeToken(int bytes = 48) => | 
					
						
							|  |  |  |         Convert.ToBase64String(RandomNumberGenerator.GetBytes(bytes)).Trim('='); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-28 15:29:18 +02:00
										 |  |  |     public static string RandomToken(int bytes = 48) => | 
					
						
							| 
									
										
										
										
											2025-02-07 21:48:50 +01:00
										 |  |  |         RandomUrlUnsafeToken(bytes) | 
					
						
							| 
									
										
										
										
											2024-12-14 16:32:08 +01:00
										 |  |  |             // Make the token URL-safe | 
					
						
							|  |  |  |             .Replace('+', '-') | 
					
						
							|  |  |  |             .Replace('/', '_'); | 
					
						
							| 
									
										
										
										
											2024-07-08 19:03:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public const int MaxAuthMethodsPerType = 3; // Maximum of 3 Discord accounts, 3 emails, etc | 
					
						
							| 
									
										
										
										
											2024-10-02 00:28:07 +02:00
										 |  |  | } |