// SPDX-FileCopyrightText: 2026 Progmasoft // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 namespace Progmasoft.OAuth.Developers; /// The type of an OAuth 2.1 application, chosen when its token is made. /// /// The type decides whether the application keeps a secret, which grants it may use and which redirection addresses /// it may register. It cannot change later: an application that changes its nature registers a new token. /// internal enum ApplicationKind { /// A web application with a server that keeps a client secret. Web, /// A browser application without a server; it cannot keep a secret. SinglePage, /// A desktop or command-line application on the user's machine; it cannot keep a secret. Desktop, /// An iOS or Android application; it cannot keep a secret. Mobile, /// A server that acts as itself, not for an account, with the client credentials grant. Service } /// The names, descriptions and rules of each . internal static class ApplicationKinds { /// Every type in the order the portal offers them. public static IReadOnlyList All { get; } = [ ApplicationKind.Web, ApplicationKind.SinglePage, ApplicationKind.Desktop, ApplicationKind.Mobile, ApplicationKind.Service ]; /// The name of a type in addresses and in the stored application. /// The type. /// The name, such as web or spa. public static string Slug(this ApplicationKind kind) => kind switch { ApplicationKind.Web => "web", ApplicationKind.SinglePage => "spa", ApplicationKind.Desktop => "desktop", ApplicationKind.Mobile => "mobile", _ => "service" }; /// Finds a type by its name. /// The name, as writes it. /// The type, or for an unknown name. public static ApplicationKind? Parse(string? slug) => All.Cast().FirstOrDefault(kind => kind!.Value.Slug() == slug); /// The title of a type in the portal. /// The type. /// The title. public static string Title(this ApplicationKind kind) => kind switch { ApplicationKind.Web => "Web application", ApplicationKind.SinglePage => "Single-page application", ApplicationKind.Desktop => "Desktop application", ApplicationKind.Mobile => "Mobile application", _ => "Service" }; /// What a type is for and how it signs in, as the portal explains it. /// The type. /// The description. public static string Description(this ApplicationKind kind) => kind switch { ApplicationKind.Web => "A site with a server. Signs people in with the authorization code flow and PKCE, and authenticates with " + "a client secret.", ApplicationKind.SinglePage => "A browser application without a server, such as React or Vue. Authorization code flow with PKCE, no " + "secret; the token endpoint answers its origin.", ApplicationKind.Desktop => "A desktop or command-line program. Authorization code flow with PKCE, no secret; returns through a " + "loopback address or a private-use scheme (RFC 8252).", ApplicationKind.Mobile => "An iOS or Android application. Authorization code flow with PKCE, no secret; returns through a " + "private-use scheme or a claimed https:// address.", _ => "A server that acts as itself, not for a person. Client credentials grant with a client secret; no " + "account scope and no sign-in page." }; /// Whether a type cannot keep a secret and proves itself with PKCE only. /// The type. /// for single-page, desktop and mobile applications. public static bool IsPublic(this ApplicationKind kind) => kind is ApplicationKind.SinglePage or ApplicationKind.Desktop or ApplicationKind.Mobile; /// Whether a type signs people in and so registers redirection addresses and scopes. /// The type. /// for a service only. public static bool SignsPeopleIn(this ApplicationKind kind) => kind is not ApplicationKind.Service; }