diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1ff0c423 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 00000000..97e538b7 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,56 @@ +name: CI/CD with Auto Versioning + +on: + workflow_dispatch: +# push: +# branches: [ master ] + +jobs: + versioning: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_version.outputs.new_version }} + steps: + - uses: actions/checkout@v4.1.2 + - name: Bump version and push tag + id: set_version + uses: mathieudutour/github-tag-action@v6.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + default_bump: patch # Automatically bump patch version + release_branches: release.*,hotfix.*,master + pre_release_branches: feature.* + + build: + needs: versioning + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.2 + - name: Setup .NET + uses: actions/setup-dotnet@v4.0.0 + with: + dotnet-version: '8.0' + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore -c Release + - name: Test + run: dotnet test --no-build -c Release --verbosity normal + - name: Pack + run: dotnet pack --no-build -c Release -o nupkg /p:PackageVersion=${{ needs.versioning.outputs.version }} + - name: Upload NuGet packages as artifacts + uses: actions/upload-artifact@v4.3.1 + with: + name: nuget-packages + path: nupkg/*.nupkg + publish: + needs: build + runs-on: ubuntu-latest + steps: + - name: Download NuGet packages artifacts + uses: actions/download-artifact@v4.1.4 + with: + name: nuget-packages + path: nupkg + - name: Push to NuGet + run: dotnet nuget push "**/*.nupkg" -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..37537f2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,371 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +/.idea/.idea.Oidc.Server.dir/.idea + +.env +/.idea/.idea.Abblix.Oidc/Docker/ +/local-npm.cmd +/local-rebuild.cmd +/Certificates/myCA/private/Abblix Licensing.pem +/LicenseGenerator diff --git a/.idea/.idea.Abblix.Oidc/.idea/.gitignore b/.idea/.idea.Abblix.Oidc/.idea/.gitignore new file mode 100644 index 00000000..53ed85cd --- /dev/null +++ b/.idea/.idea.Abblix.Oidc/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +/.idea.Abblix.Oidc.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.Abblix.Oidc/.idea/.name b/.idea/.idea.Abblix.Oidc/.idea/.name new file mode 100644 index 00000000..32fa4ab0 --- /dev/null +++ b/.idea/.idea.Abblix.Oidc/.idea/.name @@ -0,0 +1 @@ +Abblix.Oidc \ No newline at end of file diff --git a/.idea/.idea.Abblix.Oidc/.idea/encodings.xml b/.idea/.idea.Abblix.Oidc/.idea/encodings.xml new file mode 100644 index 00000000..df87cf95 --- /dev/null +++ b/.idea/.idea.Abblix.Oidc/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.Abblix.Oidc/.idea/indexLayout.xml b/.idea/.idea.Abblix.Oidc/.idea/indexLayout.xml new file mode 100644 index 00000000..7b08163c --- /dev/null +++ b/.idea/.idea.Abblix.Oidc/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.Abblix.Oidc/.idea/vcs.xml b/.idea/.idea.Abblix.Oidc/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/.idea.Abblix.Oidc/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Abblix.DependencyInjection/Abblix.DependencyInjection.csproj b/Abblix.DependencyInjection/Abblix.DependencyInjection.csproj new file mode 100644 index 00000000..cd1ba096 --- /dev/null +++ b/Abblix.DependencyInjection/Abblix.DependencyInjection.csproj @@ -0,0 +1,35 @@ + + + + net6.0;net7.0;net8.0 + enable + enable + true + true + Abblix.DependencyInjection + Abblix DependencyInjection + Enhances .NET applications by extending the standard dependency injection framework. It supports essential patterns such as service aliasing, composite services, and decorators, simplifying and enhancing service registration and resolution processes. + Abblix LLP + https://www.abblix.com/abblix-oidc-server + https://github.com/Abblix/Oidc.Server + git + Abblix DependencyInjection DI .NET ServiceLifetime Scoped Singleton Transient Composite Decorator AdvancedDI ServiceResolver .NET Core Microsoft.Extensions.DependencyInjection IoC InversionOfControl + README.md + LICENSE.md + Copyright (c) 2024 Abblix LLP. All rights reserved. + For detailed release notes, visit: https://github.com/Abblix/Oidc.Server/releases + Abblix.png + true + + + + + + + + + + + + + diff --git a/Abblix.DependencyInjection/Dependency.cs b/Abblix.DependencyInjection/Dependency.cs new file mode 100644 index 00000000..125c944d --- /dev/null +++ b/Abblix.DependencyInjection/Dependency.cs @@ -0,0 +1,104 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.Extensions.DependencyInjection; + + + +namespace Abblix.DependencyInjection; + +/// +/// Represents a dependency that can be overridden in a service provider. +/// This struct provides various static methods to create dependency overrides based on type, instance, or factory functions. +/// +public readonly struct Dependency +{ + /// + /// Creates a dependency override where a specified actual type fulfills the contract of a declared type. + /// + /// The declared type of the dependency. + /// The actual type to be used as the implementation. + /// A new instance. + public static Dependency Override() where TActual : TDeclared + => new(typeof(TDeclared), sp => ActivatorUtilities.GetServiceOrCreateInstance(sp)!); + + /// + /// Creates a dependency override with a specific instance for the declared type. + /// + /// The declared type of the dependency. + /// The instance to use for the dependency. + /// A new instance. + public static Dependency Override(TDeclared instance) + => new(typeof(TDeclared), _ => instance!); + + /// + /// Creates a dependency override using a factory function for the declared type. + /// + /// The declared type of the dependency. + /// The factory function to create the dependency instance. + /// A new instance. + public static Dependency Override(Func factory) + => new(typeof(TDeclared), sp => factory(sp)!); + + /// + /// Creates a dependency override where a specified actual type fulfills the contract of a declared type. + /// + /// The declared type of the dependency. + /// The actual type to be used as the implementation. + /// A new instance. + public static Dependency Override(Type declared, Type actual) + => new(declared, sp => ActivatorUtilities.GetServiceOrCreateInstance(sp, actual)); + + /// + /// Creates a dependency override with a specific instance for the declared type. + /// + /// The declared type of the dependency. + /// The instance to use for the dependency. + /// A new instance. + public static Dependency Override(Type declared, object instance) + => new(declared, _ => instance); + + /// + /// Creates a dependency override using a factory function for the declared type. + /// + /// The declared type of the dependency. + /// The factory function to create the dependency instance. + /// A new instance. + public static Dependency Override(Type declared, Func factory) + => new(declared, factory); + + private Dependency(Type type, Func factory) + { + Type = type; + Factory = factory; + } + + /// + /// The declared type of the dependency. + /// + internal Type Type { get; } + + /// + /// The factory function used to create the dependency instance. + /// + internal Func Factory { get; } +} \ No newline at end of file diff --git a/Abblix.DependencyInjection/ServiceCollectionExtensions.cs b/Abblix.DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..cf0aa195 --- /dev/null +++ b/Abblix.DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,268 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Abblix.DependencyInjection; + +/// +/// Provides extension methods for to enhance dependency injection capabilities. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers an alias for a service type to a specific implementation. + /// + /// The service type to be aliased. + /// The implementation type to use for the alias. + /// The to add the service to. + /// The updated . + public static IServiceCollection AddAlias(this IServiceCollection services) + where TImplementation : class, TService + where TService : class + { + var descriptor = new ServiceDescriptor( + typeof(TService), + sp => sp.GetRequiredService(), + services.GetDescriptor().Lifetime); + + services.Add(descriptor); + return services; + } + + /// + /// Composes a service type with multiple implementations into a single composite service. + /// + /// The interface type to be composed. + /// The composite implementation type. + /// The to add the service to. + /// The dependencies required by the composite service. + /// The updated . + public static IServiceCollection Compose( + this IServiceCollection services, + params Dependency[] dependencies) + where TInterface : class where TComposite : class, TInterface + { + var parameterType = typeof(TComposite) + .GetConstructors(BindingFlags.Instance | BindingFlags.Public) + .SelectMany(constructor => constructor.GetParameters(), (_, parameterInfo) => parameterInfo.ParameterType) + .FirstOrDefault(type => type.IsAssignableFrom(typeof(TInterface[]))); + + if (parameterType == null) + throw new InvalidOperationException( + $"The type {typeof(TComposite).FullName} has no public constructor that accepts {typeof(TInterface).FullName}[]"); + + var serviceDescriptors = services + .Where(descriptor => descriptor.ServiceType == typeof(TInterface)) + .ToArray(); + + if (serviceDescriptors.Length <= 1) + return services; + + // choose the shortest lifetime among existing service registrations + var lifetime = serviceDescriptors.Max(descriptor => descriptor.Lifetime); + + var compositeDescriptor = ServiceDescriptor.Describe( + typeof(TInterface), + serviceProvider => + { + var serviceInstances = Array.ConvertAll( + serviceDescriptors, + serviceDescriptor => (TInterface)serviceProvider.CreateService(serviceDescriptor)); + + var serviceDependencies = Dependency.Override(parameterType, serviceInstances); + return serviceProvider.CreateService(dependencies.Append(serviceDependencies)); + }, + lifetime); + + services.RemoveAll(); + services.Add(compositeDescriptor); + + return services; + } + + /// + /// Decorates a registered service with a decorator implementation. + /// + /// The service type to be decorated. + /// The decorator implementation type. + /// The to add the service to. + /// The dependencies required by the decorator. + /// The updated . + public static IServiceCollection Decorate( + this IServiceCollection services, + params Dependency[] dependencies) + where TInterface : class where TDecorator : class, TInterface + { + var serviceDescriptor = services.GetDescriptor(); + + var decoratorDescriptor = ServiceDescriptor.Describe( + typeof(TInterface), + serviceProvider => + { + var instance = Dependency.Override((TInterface)serviceProvider.CreateService(serviceDescriptor)); + return serviceProvider.CreateService(dependencies.Append(instance)); + }, + serviceDescriptor.Lifetime); + + return services.Replace(decoratorDescriptor); + } + + private static T[] Append(this T[] source, T element) + { + switch (source) + { + case { Length: > 0 }: + Array.Resize(ref source, source.Length + 1); + source[^1] = element; + return source; + + default: + return new[] { element }; + } + } + + private static ServiceDescriptor GetDescriptor(this IServiceCollection services) + where TInterface : class + { + return services.SingleOrDefault(s => s.ServiceType == typeof(TInterface)) + ?? throw new InvalidOperationException($"{typeof(TInterface).Name} is not registered"); + } + + private static object CreateService(this IServiceProvider serviceProvider, ServiceDescriptor descriptor) + { + return descriptor switch + { + { ImplementationInstance: { } instance } => instance, + { ImplementationFactory: { } factory } => factory(serviceProvider), + { ImplementationType: { } type } => ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, type), + _ => throw new InvalidOperationException($"Unable to create instance of {descriptor.ServiceType.FullName}") + }; + } + + public static T CreateService(this IServiceProvider serviceProvider, params Dependency[] dependencies) + { + return (T)serviceProvider.CreateService(typeof(T), dependencies); + } + + public static object CreateService(this IServiceProvider serviceProvider, + Type type, params Dependency[] dependencies) + { + var factory = ActivatorUtilities.CreateFactory(type, Array.ConvertAll(dependencies, d => d.Type)); + return factory(serviceProvider, Array.ConvertAll(dependencies, d => d.Factory(serviceProvider))); + } + + /// + /// Registers a transient service with custom dependencies. + /// + /// The service type to register. + /// The to add the service to. + /// The dependencies required by the service. + /// The updated . + public static IServiceCollection AddTransient(this IServiceCollection services, params Dependency[] dependencies) + where T : class + { + return services.AddTransient(sp => sp.CreateService(dependencies)); + } + + /// + /// Registers a transient service with custom dependencies. + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The to add the service to. + /// The dependencies required by the service. + /// The updated . + public static IServiceCollection AddTransient(this IServiceCollection services, + params Dependency[] dependencies) + where TService : class + where TImplementation : class, TService + { + return services.AddTransient(sp => sp.CreateService(dependencies)); + } + + /// + /// Registers a scoped service of the type specified in with custom dependencies. + /// A scoped service is created once per request within the scope. + /// + /// The type of the service to add. + /// The to add the service to. + /// An array of objects representing additional dependencies required by the service. + /// The updated . + public static IServiceCollection AddScoped(this IServiceCollection services, params Dependency[] dependencies) + where T : class + { + return services.AddScoped(sp => sp.CreateService(dependencies)); + } + + /// + /// Registers a scoped service with the implementation type specified in + /// and the service type specified in with custom dependencies. + /// A scoped service is created once per request within the scope. + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The to add the service to. + /// An array of objects representing additional dependencies required by the service. + /// The updated . + public static IServiceCollection AddScoped(this IServiceCollection services, + params Dependency[] dependencies) + where TService : class + where TImplementation : class, TService + { + return services.AddScoped(sp => sp.CreateService(dependencies)); + } + + /// + /// Registers a singleton service of the type specified in with custom dependencies. + /// A singleton service is created the first time it is requested, and subsequent requests use the same instance. + /// + /// The type of the service to add. + /// The to add the service to. + /// An array of objects representing additional dependencies required by the service. + /// The updated . + public static IServiceCollection AddSingleton(this IServiceCollection services, params Dependency[] dependencies) + where T : class + { + return services.AddSingleton(sp => sp.CreateService(dependencies)); + } + + /// + /// Registers a singleton service of the type specified in with custom dependencies. + /// A singleton service is created the first time it is requested, and subsequent requests use the same instance. + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The to add the service to. + /// An array of objects representing additional dependencies required by the service. + /// The updated . + + public static IServiceCollection AddSingleton(this IServiceCollection services, + params Dependency[] dependencies) + where TService : class + where TImplementation : class, TService + { + return services.AddSingleton(sp => sp.CreateService(dependencies)); + } +} \ No newline at end of file diff --git a/Abblix.Jwt.UnitTests/Abblix.Jwt.UnitTests.csproj b/Abblix.Jwt.UnitTests/Abblix.Jwt.UnitTests.csproj new file mode 100644 index 00000000..f7c1ef1a --- /dev/null +++ b/Abblix.Jwt.UnitTests/Abblix.Jwt.UnitTests.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Abblix.Jwt.UnitTests/JwtEncryptionTests.cs b/Abblix.Jwt.UnitTests/JwtEncryptionTests.cs new file mode 100644 index 00000000..9bc4302c --- /dev/null +++ b/Abblix.Jwt.UnitTests/JwtEncryptionTests.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Utils; +using Microsoft.IdentityModel.Tokens; +using Xunit; + +namespace Abblix.Jwt.UnitTests; + +public class JwtEncryptionTests +{ + private static readonly JsonWebKey EncryptingKey = JsonWebKeyFactory.CreateRsa(JsonWebKeyUseNames.Enc); + private static readonly JsonWebKey SigningKey = JsonWebKeyFactory.CreateRsa(JsonWebKeyUseNames.Sig); + + [Fact] + public async Task JwtFullCycleTest() + { + var issuedAt = DateTimeOffset.UtcNow; + + var token = new JsonWebToken + { + Header = { Algorithm = SigningAlgorithms.RS256 }, + Payload = { + JwtId = Guid.NewGuid().ToString("N"), + IssuedAt = issuedAt, + NotBefore = issuedAt, + ExpiresAt = issuedAt + TimeSpan.FromDays(1), + Issuer = "abblix.com", + Audiences = new []{ nameof(JwtFullCycleTest) }, + ["test"] = "value", + ["address"] = new JsonObject + { + { "street", "123 Main St" }, + { "city", "Springfield" }, + { "state", "IL" }, + { "zip", "62701" }, + } + }, + }; + + var creator = new JsonWebTokenCreator(); + var jwt = await creator.IssueAsync(token, SigningKey, EncryptingKey); + + var validator = new JsonWebTokenValidator(); + var parameters = new ValidationParameters + { + ValidateAudience = aud => Task.FromResult(token.Payload.Audiences.SequenceEqual(aud)), + ValidateIssuer = iss => Task.FromResult(iss == token.Payload.Issuer), + ResolveTokenDecryptionKeys = _ => new [] { EncryptingKey }.AsAsync(), + ResolveIssuerSigningKeys = _ => new [] { SigningKey }.AsAsync(), + }; + + var result = Assert.IsType(await validator.ValidateAsync(jwt, parameters)); + var expectedClaims = ExtractClaims(token); + var actualClaims = ExtractClaims(result.Token); + Assert.Equal(expectedClaims, actualClaims); + } + + private static IEnumerable<(string Key, string?)> ExtractClaims(JsonWebToken token) + => from claim in token.Payload.Json + select (claim.Key, claim.Value?.ToJsonString()); +} diff --git a/Abblix.Jwt/Abblix.Jwt.csproj b/Abblix.Jwt/Abblix.Jwt.csproj new file mode 100644 index 00000000..0357cc7b --- /dev/null +++ b/Abblix.Jwt/Abblix.Jwt.csproj @@ -0,0 +1,40 @@ + + + + net6.0;net7.0;net8.0 + enable + enable + true + true + Abblix.JWT + Abblix JWT + A lightweight, easy-to-use library for working with JSON Web Tokens (JWT) in .NET applications. Features include token validation, custom claims, signature verification, and audience validation, making it an ideal choice for developers looking to secure their .NET applications efficiently. + Abblix LLP + https://www.abblix.com/abblix-oidc-server + https://github.com/Abblix/Oidc.Server + git + Abblix JWT JSON-Web-Token Authentication Security Token API-Security OAuth OAuth2 AccessToken Identity ASP.NET-Security Web-Security + README.md + LICENSE.md + Copyright (c) 2024 Abblix LLP. All rights reserved. + For detailed release notes, visit: https://github.com/Abblix/Oidc.Server/releases + Abblix.png + true + + + + + + + + + + + + + + + + + + diff --git a/Abblix.Jwt/AlgorithmMapper.cs b/Abblix.Jwt/AlgorithmMapper.cs new file mode 100644 index 00000000..a127907c --- /dev/null +++ b/Abblix.Jwt/AlgorithmMapper.cs @@ -0,0 +1,64 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.IdentityModel.Tokens; + + + +namespace Abblix.Jwt; + +/// +/// Provides functionality to map signing algorithm names to their corresponding outbound algorithm names. +/// +public static class AlgorithmMapper +{ + /// + /// Maps a given signing algorithm name to the corresponding outbound algorithm name. + /// + /// The signing algorithm name to map. + /// The outbound algorithm name corresponding to the given signing algorithm. + /// + /// This mapping is used to standardize algorithm names across different contexts or specifications, + /// ensuring consistency in cryptographic operations. + /// + public static string MapToOutbound(string signingAlgorithm) => signingAlgorithm switch + { + SecurityAlgorithms.EcdsaSha256Signature => SecurityAlgorithms.EcdsaSha256, + SecurityAlgorithms.EcdsaSha384Signature => SecurityAlgorithms.EcdsaSha384, + SecurityAlgorithms.EcdsaSha512Signature => SecurityAlgorithms.EcdsaSha512, + + SecurityAlgorithms.HmacSha256Signature => SecurityAlgorithms.HmacSha256, + SecurityAlgorithms.HmacSha384Signature => SecurityAlgorithms.HmacSha384, + SecurityAlgorithms.HmacSha512Signature => SecurityAlgorithms.HmacSha512, + + SecurityAlgorithms.RsaSha256Signature => SecurityAlgorithms.RsaSha256, + SecurityAlgorithms.RsaSha384Signature => SecurityAlgorithms.RsaSha384, + SecurityAlgorithms.RsaSha512Signature => SecurityAlgorithms.RsaSha512, + + SecurityAlgorithms.Aes128KeyWrap => SecurityAlgorithms.Aes128KW, + SecurityAlgorithms.Aes192KeyWrap => SecurityAlgorithms.Aes256KW, + SecurityAlgorithms.RsaV15KeyWrap => SecurityAlgorithms.RsaPKCS1, + SecurityAlgorithms.RsaOaepKeyWrap => SecurityAlgorithms.RsaOAEP, + + _ => signingAlgorithm, + }; +} diff --git a/Abblix.Jwt/ClaimExtensions.cs b/Abblix.Jwt/ClaimExtensions.cs new file mode 100644 index 00000000..44c8f785 --- /dev/null +++ b/Abblix.Jwt/ClaimExtensions.cs @@ -0,0 +1,45 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + + + +namespace Abblix.Jwt; + +/// +/// Provides extension methods for handling claims, particularly for converting between JWT claims and security claims. +/// +public static class ClaimExtensions +{ + /// + /// Converts a to a string representation. If the node is a , + /// the value is extracted as a string; otherwise, the JSON string representation of the node is returned. + /// + /// The to convert to a string. + /// A string representation of the . + public static string AsString(this JsonNode node) => node switch + { + JsonValue value when value.TryGetValue(out string? s) => s, + _ => node.ToJsonString(), + }; +} diff --git a/Abblix.Jwt/IJsonWebTokenCreator.cs b/Abblix.Jwt/IJsonWebTokenCreator.cs new file mode 100644 index 00000000..92d8bf61 --- /dev/null +++ b/Abblix.Jwt/IJsonWebTokenCreator.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Defines the contract for a service that creates JSON Web Tokens (JWTs). +/// +public interface IJsonWebTokenCreator +{ + /// + /// Lists the all supported signing algorithms for JWT creation. + /// + IEnumerable SigningAlgValuesSupported { get; } + + /// + /// Issues a new JWT based on the specified JsonWebToken object, signing key, and optional encrypting key. + /// + /// The JsonWebToken object containing the payload of the JWT. + /// The JsonWebKey used to sign the JWT. + /// Optional JsonWebKey used to encrypt the JWT. If null, the JWT is not encrypted. + /// A Task representing the asynchronous operation, which upon completion yields the JWT as a string. + Task IssueAsync(JsonWebToken jwt, JsonWebKey? signingKey, JsonWebKey? encryptingKey = null); +} diff --git a/Abblix.Jwt/IJsonWebTokenValidator.cs b/Abblix.Jwt/IJsonWebTokenValidator.cs new file mode 100644 index 00000000..80430fcc --- /dev/null +++ b/Abblix.Jwt/IJsonWebTokenValidator.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Defines the contract for a service that validates JSON Web Tokens (JWTs). +/// +public interface IJsonWebTokenValidator +{ + /// + /// Indicates which algorithms are accepted by the validator for verifying the signatures of incoming JWTs, + /// ensuring that only tokens signed with recognized and secure algorithms are considered valid. + /// + IEnumerable SigningAlgValuesSupported { get; } + + /// + /// Asynchronously validates a JWT against a set of specified parameters. + /// + /// The JWT as a string to be validated. + /// The parameters against which the JWT will be validated. + /// A Task representing the asynchronous validation operation, which yields a JwtValidationResult indicating the outcome of the validation. + Task ValidateAsync(string jwt, ValidationParameters parameters); +} diff --git a/Abblix.Jwt/IanaClaimTypes.cs b/Abblix.Jwt/IanaClaimTypes.cs new file mode 100644 index 00000000..91762bff --- /dev/null +++ b/Abblix.Jwt/IanaClaimTypes.cs @@ -0,0 +1,870 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Provides constants for various JWT and OpenID Connect claim types. +/// Includes both registered claim types and public claim types as defined in various standards. +/// This classification helps in ensuring interoperability across different systems and services +/// by adhering to a common set of identifiers for claims. +/// +public static class IanaClaimTypes +{ + /// + /// A set of predefined claims recommended for ensuring interoperability among different systems. + /// These claims are widely recognized and provide basic information necessary + /// for many authentication and authorization processes. + /// + public static readonly IReadOnlyCollection Registered = new HashSet(StringComparer.OrdinalIgnoreCase) + { + Iss, Sub, Aud, Exp, Nbf, Iat, Jti + }; + + /// + /// A set of public claims that can be defined by applications as needed. + /// To ensure global uniqueness and avoid collisions, these claims should either be registered + /// with the IANA JSON Web Token Registry or be defined within a namespace that is resistant to collisions, + /// such as a URI. + /// + public static readonly IReadOnlyCollection Public = new HashSet(StringComparer.OrdinalIgnoreCase) + { + Name, GivenName, FamilyName, MiddleName, Nickname, PreferredUsername, Profile, Picture, Website, Email, + EmailVerified, Gender, Birthdate, Zoneinfo, Locale, PhoneNumber, PhoneNumberVerified, Address, UpdatedAt, Azp, + Nonce, AuthTime, AtHash, CHash, Acr, Amr, SubJwk, Cnf, SipFromTag, SipDate, SipCallid, SipCseqNum, SipViaBranch, + Orig, Dest, Mky, Events, Toe, Txn, Rph, Sid, Vot, Vtm, Attest, Origid, Act, Scope, ClientId, MayAct, Jcard, + AtUseNbr, Div, Opt, Vc, Vp, Sph, AceProfile, Cnonce, Exi, Roles, Groups, Entitlements, TokenIntrospection, + Cdniv, Cdnicrit, Cdniip, Cdniuc, Cdniets, Cdnistt, Cdnistd, SigValClaims, AuthorizationDetails + }; + + #region RFC7519, Section 4.1.1 - Issuer Claim + + /// + /// Represents the principal (e.g., authorization server) that issued the JWT. + /// + /// + /// Defined in RFC 7519, Section 4.1.1. It is a case-sensitive string containing a StringOrURI value. + /// Use this claim to identify the issuer of the JWT uniquely. + /// + public const string Iss = "iss"; + + #endregion + + #region RFC7519, Section 4.1.2 - Subject Claim + + /// + /// Represents the principal that is the subject of the JWT. + /// + /// + /// Defined in RFC 7519, Section 4.1.2. The "sub" value is a case-sensitive string containing a StringOrURI value. + /// This claim is used to identify the subject of the JWT, which could be an end user or a device. + /// + public const string Sub = "sub"; + + #endregion + + #region RFC7519, Section 4.1.3 - Audience Claim + + /// + /// Identifies the recipients that the JWT is intended for. + /// + /// + /// Defined in RFC 7519, Section 4.1.3. It is generally a case-sensitive string or an array of strings containing StringOrURI values. + /// The audience claim ensures that the JWT is sent to the intended recipients. + /// + public const string Aud = "aud"; + + #endregion + + #region RFC7519, Section 4.1.4 - Expiration Time Claim + + /// + /// Specifies the expiration time on or after which the JWT must not be accepted for processing. + /// + /// + /// Defined in RFC 7519, Section 4.1.4. The "exp" claim is a NumericDate value. Use this claim to define the validity period of the JWT. + /// + public const string Exp = "exp"; + + #endregion + + #region RFC7519, Section 4.1.5 - Not Before Claim + + /// + /// Defines a time before which the JWT MUST NOT be accepted for processing. + /// + /// + /// Defined in RFC 7519, Section 4.1.5. The "nbf" (Not Before) claim is a NumericDate value. + /// This claim helps in ensuring that a JWT is not accepted before a certain time. + /// + public const string Nbf = "nbf"; + + #endregion + + #region RFC7519, Section 4.1.6 - Issued At Claim + + /// + /// Indicates the time at which the JWT was issued. + /// + /// + /// Defined in RFC 7519, Section 4.1.6. The "iat" (Issued At) claim is a NumericDate value. + /// This claim can be used to determine the age of the JWT. + /// + public const string Iat = "iat"; + + #endregion + + #region RFC7519, Section 4.1.7 - JWT ID Claim + + /// + /// Provides a unique identifier for the JWT. + /// + /// + /// Defined in RFC 7519, Section 4.1.7. The "jti" (JWT ID) claim is a case-sensitive string. + /// Use this claim to prevent the JWT from being replayed. + /// + public const string Jti = "jti"; + + #endregion + + #region OpenID Connect Core 1.0, Section 5.1 - Personal Identifiable Information Claims + + /// + /// Represents the full name of the user. + /// + /// + /// Used in OpenID Connect for representing the user's full name in a single string. It might include the first, middle, last, and other names. + /// + public const string Name = "name"; + + /// + /// Represents the first or given name(s) of the user. + /// + /// + /// Used in OpenID Connect. This claim is intended to refer to the user's first name or given name(s). It allows for middle names if applicable. + /// + public const string GivenName = "given_name"; + + /// + /// Represents the surname(s) or last name(s) of the user. + /// + /// + /// Used in OpenID Connect. This claim focuses on the user's family name or surname(s), excluding middle names. + /// + public const string FamilyName = "family_name"; + + /// + /// Represents the middle name(s) of the user. + /// + /// + /// Used in OpenID Connect. This claim is intended for the user's middle name(s), which might not be present for all users. + /// + public const string MiddleName = "middle_name"; + + /// + /// Represents the casual name of the user. + /// + /// + /// Used in OpenID Connect. This claim is for the user's casual or informal name that might differ from their legal name. + /// + public const string Nickname = "nickname"; + + /// + /// The username preferred by the user, which may be different from their actual or legal name. + /// + /// + /// This claim is used to convey the user's preferred username within the system. It allows the user + /// to specify a nickname or alias that is used within the application for display purposes, + /// providing a more personalized user experience. + /// + public const string PreferredUsername = "preferred_username"; + + /// + /// Represents the URL of the user's profile page. + /// + /// + /// Used in OpenID Connect. It is the URL of a web page containing information about the user or a social profile page. + /// + public const string Profile = "profile"; + + /// + /// Represents the URL of the user's profile picture. + /// + /// + /// Used in OpenID Connect. This claim provides a URL pointing to a profile picture or avatar of the user. + /// + public const string Picture = "picture"; + + /// + /// Represents the URL of the user's web page or blog. + /// + /// + /// Used in OpenID Connect. This claim indicates the URL of the user's personal or business website. + /// + public const string Website = "website"; + + /// + /// Represents the user's preferred email address. + /// + /// + /// Used in OpenID Connect. This claim is for the user's preferred email. Note that this email address might not be unique. + /// + public const string Email = "email"; + + /// + /// Represents whether the user's email address has been verified. + /// + /// + /// Used in OpenID Connect as a boolean. True if the user's email address has been verified; otherwise false. + /// + public const string EmailVerified = "email_verified"; + + /// + /// Represents the user's gender. + /// + /// + /// Used in OpenID Connect. This claim can be used to convey the user's gender. The value is not strictly defined and can vary based on the user's preference and the application's requirements. + /// + public const string Gender = "gender"; + + /// + /// Represents the user's date of birth. + /// + /// + /// Used in OpenID Connect. This claim is for the user's birthdate, typically represented in the ISO 8601:2004 YYYY-MM-DD format. + /// + public const string Birthdate = "birthdate"; + + /// + /// Represents the user's time zone. + /// + /// + /// Used in OpenID Connect. This claim indicates the user's time zone, facilitating localization and personalization. + /// + public const string Zoneinfo = "zoneinfo"; + + /// + /// Represents the user's locale. + /// + /// + /// Used in OpenID Connect. This claim specifies the user's preferred language and optionally, region. Typically represented as a language tag, e.g., en-US or fr-CA. + /// + public const string Locale = "locale"; + + /// + /// Represents the user's phone number. + /// + /// + /// Used in OpenID Connect. This claim provides the user's preferred phone number. The format of the number can vary and it's not guaranteed to be in a standard format. + /// + public const string PhoneNumber = "phone_number"; + + /// + /// Indicates whether the user's phone number has been verified. + /// + /// + /// Used in OpenID Connect as a boolean. True if the user's phone number has been verified; otherwise false. + /// + public const string PhoneNumberVerified = "phone_number_verified"; + + /// + /// Represents the user's postal address. + /// + /// + /// Used in OpenID Connect. This JSON structured claim contains components of the user's address such as street address, locality, region, postal code, and country. + /// + public const string Address = "address"; + + /// + /// Indicates when the user's information was last updated. + /// + /// + /// Used in OpenID Connect. This claim provides a Unix time stamp indicating when the user's information was last updated. + /// + public const string UpdatedAt = "updated_at"; + + #endregion + + #region OpenID Connect Core 1.0, Section 2 - Other Claims + + /// + /// Represents the authorized party - the party to which the ID Token was issued. + /// + /// + /// Used in OpenID Connect. This claim is particularly useful in delegated scenarios to identify the party using the ID Token. + /// + public const string Azp = "azp"; + + /// + /// A string value used to associate a client session with an ID Token. + /// + /// + /// Used in OpenID Connect to mitigate replay attacks by binding a session to a token. + /// + public const string Nonce = "nonce"; + + /// + /// Indicates the time when the authentication occurred. + /// + /// + /// Used in OpenID Connect. This claim is critical in scenarios where the application requires assurance about + /// the moment of authentication, such as re-authentication or step-up authentication. + /// + public const string AuthTime = "auth_time"; + + /// + /// Represents the hash of the access token issued. + /// + /// + /// Used in OpenID Connect. This claim is included in the ID Token and is a hash of the access token, + /// allowing the recipient to validate the integrity of the access token. It is particularly useful + /// in implicit and authorization code flows. + /// + public const string AtHash = "at_hash"; + + /// + /// Represents the Authentication Context Class Reference. + /// + /// + /// Used in OpenID Connect. This claim specifies the authentication context class that the authentication + /// performed satisfied. It allows clients to request and services to assert the strength of an authentication process. + /// + public const string Acr = "acr"; + + /// + /// Represents the Authentication Methods References. + /// + /// + /// Used in OpenID Connect. This claim is used to specify the authentication methods used in the authentication process. + /// It provides transparency about how the authentication was performed, such as 'pwd' for password-based + /// or 'mfa' for multi-factor authentication. + /// + public const string Amr = "amr"; + + #endregion + + #region OpenID Connect Core 1.0, Section 3.3.2.11 + + /// + /// Represents the Code Hash Value. + /// + /// + /// Used in OpenID Connect. This claim is used in the ID Token to provide a hash of the authorization code. + /// It ensures that the authorization code is bound to the ID Token, enhancing the security of the code exchange process. + /// + public const string CHash = "c_hash"; + + #endregion + + #region OpenID Connect Core 1.0, Section 7.4 + + /// + /// Represents the subject's public key as a JSON Web Key (JWK). + /// + /// + /// Used in scenarios where public keys are associated with JWT subjects. + /// This claim allows embedding a public key directly within a JWT, facilitating key discovery and distribution. + /// + public const string SubJwk = "sub_jwk"; + + #endregion + + #region RFC7800, Section 3.1 + + /// + /// Represents confirmation methods used by the token. + /// + /// + /// Utilized in scenarios that require additional confirmation of token validity, such as DPoP + /// (Demonstrating Proof of Possession). This claim helps in binding tokens to specific cryptographic keys. + /// + public const string Cnf = "cnf"; + + #endregion + + #region RFC8055 - SIP Claims + + /// + /// "sip_from_tag" - Value from the SIP 'From' tag header field, used in SIP-based communications. + /// + public const string SipFromTag = "sip_from_tag"; + + /// + /// "sip_date" - Value from the SIP 'Date' header field, indicating the time of the SIP message. + /// + public const string SipDate = "sip_date"; + + /// + /// "sip_callid" - Value from the SIP 'Call-Id' header field, uniquely identifying the SIP call. + /// + public const string SipCallid = "sip_callid"; + + /// + /// "sip_cseq_num" - Numeric value from the SIP 'CSeq' header field, indicating the command sequence number in SIP protocol. + /// + public const string SipCseqNum = "sip_cseq_num"; + + /// + /// "sip_via_branch" - Value from the SIP 'Via' branch parameter, used in routing SIP messages. + /// + public const string SipViaBranch = "sip_via_branch"; + + #endregion + + #region RFC8225, Section 5.2.1 - 5.2.2 + + /// + /// Represents the originating identity in telecommunication protocols. + /// + /// + /// It's used to convey the identity of the originator in communication protocols, aiding in the identification + /// and verification of the call origin for security and billing purposes. + /// + public const string Orig = "orig"; + + /// + /// Represents the destination identity in telecommunication protocols. + /// + /// + /// Useful in communication protocols to convey the intended recipient or destination of the communication, + /// supporting routing, billing, and security measures. + /// + public const string Dest = "dest"; + + /// + /// Represents a media key fingerprint. + /// + /// + /// Used in secure communication protocols to provide a fingerprint of the cryptographic key used for + /// encrypting media, enhancing the security of media exchanges by facilitating key verification. + /// + public const string Mky = "mky"; + + #endregion + + #region RFC8417, Section 2.2 - Security Event Tokens + + /// + /// Represents specific security events or state changes. + /// + /// + /// Used in Security Event Tokens (SETs) to convey information about security-related events or changes, + /// such as authentication events or configuration changes, aiding in security monitoring and response. + /// + public const string Events = "events"; + + /// + /// Represents the time of the security event. + /// + /// + /// This claim is used in Security Event Tokens (SETs) to indicate the precise time at which the described + /// security event occurred, facilitating accurate incident tracking and response. + /// + public const string Toe = "toe"; + + /// + /// Represents a transaction identifier. + /// + /// + /// Often utilized in financial transactions and other scenarios where tracking the identity and state + /// of individual transactions is critical for security, auditing, and reconciliation processes. + /// + public const string Txn = "txn"; + + #endregion + + #region RFC8443, Section 3 - Resource Priority + + /// + /// Represents a resource priority header. + /// + /// + /// This claim is used to indicate the priority of a resource or process in network communications, + /// ensuring that critical resources receive appropriate handling and prioritization in congested or + /// limited-capacity environments. + /// + public const string Rph = "rph"; + + #endregion + + #region OpenID Connect Front-Channel Logout 1.0, Section 3 + + /// + /// Represents the session ID for front-channel logout in OpenID Connect sessions. + /// + /// + /// This claim is critical for implementing front-channel logout mechanisms, allowing clients and servers + /// to coordinate user sessions and logout processes across multiple applications and services. + /// + public const string Sid = "sid"; + + #endregion + + #region rfc8485 - Vector of Trust + + /// + /// Represents the vector of trust for authentication processes. + /// + /// + /// Used to convey the level of confidence in the authentication process, detailing the methods used and + /// their security properties. This claim is particularly useful in contexts requiring a nuanced understanding + /// of authentication assurance. + /// + public const string Vot = "vot"; + + /// + /// Represents the vector of trust trustmark. + /// + /// + /// Provides a URL to a trustmark that further describes the trust vector associated with the authentication process, + /// offering a means to verify the authentication methods and their adherence to certain standards or practices. + /// + public const string Vtm = "vtm"; + + #endregion + + #region rfc8588 - SHAKEN Framework + + /// + /// Represents the attestation level in SHAKEN/STIR frameworks. + /// + /// + /// This claim is used within the SHAKEN/STIR framework for telecommunication services, indicating the level + /// of attestation for the origin of a call, which aids in combating caller ID spoofing and fraud. + /// + public const string Attest = "attest"; + + /// + /// Represents the originating identifier in the SHAKEN framework. + /// + /// + /// Used to uniquely identify the originator of a call within the SHAKEN framework, facilitating traceability + /// and verification of the call's origin, and enhancing trust in telecommunication ecosystems. + /// + public const string Origid = "origid"; + + #endregion + + #region RFC8693 - OAuth 2.0 Token Exchange + + /// + /// Represents the actor in OAuth 2.0 token exchange. + /// + /// + /// This claim is used to indicate the party that the token represents, especially in delegation and + /// impersonation scenarios, allowing APIs and services to verify the actual party making a request. + /// + public const string Act = "act"; + + /// + /// Represents the scope associated with an access token. + /// + /// + /// Specifies the permissions or access rights granted to an access token, defining what actions + /// the token bearer is authorized to perform. This claim is fundamental in controlling access to resources. + /// + public const string Scope = "scope"; + + /// + /// Represents the client identifier in OAuth 2.0 contexts. + /// + /// + /// Identifies the OAuth 2.0 client that requested the token, providing a mechanism for associating a token + /// with a specific registered client application, critical for enforcing client-specific access policies. + /// + public const string ClientId = "client_id"; + + /// + /// Indicates the parties that the token bearer is authorized to act on behalf of. + /// + /// + /// This claim is used in scenarios where a token bearer is permitted to act on behalf of other parties, + /// enabling delegation of rights and facilitating advanced authorization scenarios. + /// + public const string MayAct = "may_act"; + + #endregion + + #region rfc8688 - jCard Data + + /// + /// Contains jCard data, representing contact information in a JSON format. + /// + /// + /// This claim is used to convey contact information in a structured format that mirrors the vCard specification, + /// facilitating interoperable exchange of personal or organizational contact details. + /// + public const string Jcard = "jcard"; + + #endregion + + #region ETSI GS NFV-SEC 022 - API Request Number + + /// + /// Indicates the number of API requests for which the access token can be used. + /// + /// + /// This claim is used primarily in environments where token usage is tightly controlled and monitored, + /// providing a mechanism for limiting the number of requests a token can authorize to enhance security and + /// manage resource utilization. + /// + public const string AtUseNbr = "at_use_nbr"; + + #endregion + + #region rfc8946 - Diverted Call Information + + + /// + /// Contains information about a call that was diverted from its original destination. + /// + /// + /// This claim is used in telecommunications contexts to provide details about call diversions, + /// aiding in the management and tracing of call flows and in the implementation of services that + /// react to call redirection. + /// + public const string Div = "div"; + + /// + /// Contains the original PASSporT in full form, often used in call diversion scenarios. + /// + /// + /// This claim is utilized in telecommunications to verify the authenticity of redirected calls by providing + /// a cryptographic assertion of the call's origin, enhancing trust and security in voice communications. + /// + public const string Opt = "opt"; + + #endregion + + #region W3C Verifiable Credentials + + /// + /// Represents a verifiable credential as specified in the W3C Recommendation. + /// + /// + /// This claim is used to convey credentials that can be cryptographically verified, supporting a wide range + /// of applications from identity verification to qualification attestation in a secure and interoperable manner. + /// + public const string Vc = "vc"; + + /// + /// Represents a verifiable presentation as specified in the W3C Recommendation. + /// + /// + /// This claim is used when a subject presents one or more verifiable credentials, allowing the verifier + /// to check the authenticity and integrity of the credentials presented, facilitating trusted digital interactions. + /// + public const string Vp = "vp"; + + #endregion + + #region rfc9027 - SIP Priority Header + + /// + /// Used to indicate the priority of a SIP message. + /// + /// + /// This claim is relevant in Session Initiation Protocol (SIP)-based communications, where it may influence routing, + /// handling, and processing priorities of SIP messages, ensuring that critical communications are appropriately + /// prioritized. + /// + public const string Sph = "sph"; + + #endregion + + #region RFC9200 - ACE Framework + + /// + /// Specifies the ACE profile a token is used with, indicating its application in constrained environments. + /// + /// + /// This claim is significant in scenarios utilizing the Authentication and Authorization for Constrained + /// Environments (ACE) framework, ensuring that tokens are applied in accordance with the specific requirements + /// and constraints of the ACE profile in use. + /// + public const string AceProfile = "ace_profile"; + + /// + /// Nonce provided by the Resource Server to the Authorization Server via the client, verifying token freshness. + /// + /// + /// This claim is used to ensure the freshness of a token in interactions between the Resource Server, the client, + /// and the Authorization Server, mitigating against replay attacks by validating the uniqueness and timeliness + /// of each token request. + /// + public const string Cnonce = "cnonce"; + + /// + /// Lifetime of the token in seconds from the time the Resource Server first sees it, for devices with + /// unsynchronized clocks. + /// + /// + /// This claim addresses challenges posed by devices with unsynchronized clocks by providing a relative measure + /// of the token's validity, enhancing interoperability and security in distributed systems. + /// + public const string Exi = "exi"; + + #endregion + + #region RFC7643 - SCIM Roles and Groups + + /// + /// Represents the roles associated with the subject, often used in System for Cross-domain Identity Management (SCIM). + /// + /// + /// This claim is used to convey the roles attributed to a subject, facilitating role-based access control (RBAC) + /// and other authorization decisions in systems implementing SCIM or similar identity management protocols. + /// + public const string Roles = "roles"; + + /// + /// Represents the groups that the subject belongs to, typically used in identity and access management. + /// + /// + /// This claim is used to convey group membership information, supporting group-based access control and + /// enabling systems to make authorization decisions based on the groups a subject is associated with. + /// + public const string Groups = "groups"; + + /// + /// Represents specific entitlements or permissions granted to the subject. + /// + /// + /// This claim is used to specify granular permissions or entitlements granted to a subject, + /// allowing for precise control over access rights and enabling fine-grained authorization policies. + /// + public const string Entitlements = "entitlements"; + + #endregion + + #region OAuth JWT Introspection + + /// + /// Contains the response from an OAuth 2.0 token introspection request. + /// + /// + /// This claim is typically used in scenarios where detailed token information is necessary for validating + /// token status, scopes, and other attributes as part of OAuth 2.0 introspection processes. + /// + public const string TokenIntrospection = "token_introspection"; + + #endregion + + #region CDNI Claims - RFC9246 + + /// + /// Version of the claim set used in Content Delivery Network Interconnection (CDNI). + /// + /// + /// This claim facilitates interoperability in CDNI contexts by specifying the version of the claim set, + /// ensuring that both the issuer and recipient of a JWT understand the structure and semantics of + /// the claims contained within. + /// + public const string Cdniv = "cdniv"; + + /// + /// Identifies critical claims within the CDNI claim set. + /// + /// + /// This claim is used to mark certain claims as critical within the context of CDNI, + /// indicating that the JWT should be processed differently or not at all if these claims are not understood + /// or cannot be fulfilled. + /// + public const string Cdnicrit = "cdnicrit"; + + /// + /// Represents an IP address in CDNI scenarios. + /// + /// + /// This claim is used to convey IP address information within CDNI, + /// supporting operations such as tokenized redirection or content delivery optimizations based + /// on geographical or network-based criteria. + /// + public const string Cdniip = "cdniip"; + + /// + /// Contains a URI in CDNI contexts. + /// + /// + /// This claim is utilized to reference specific resources or content within CDNI, allowing for dynamic + /// content delivery configurations and optimizations based on the content identified by the URI. + /// + public const string Cdniuc = "cdniuc"; + + /// + /// Expiration time setting for token renewal in CDNI. + /// + /// + /// This claim specifies the expiration time for a CDNI token, guiding the renewal process by indicating + /// when a new token must be obtained to continue accessing CDNI content or services. + /// + public const string Cdniets = "cdniets"; + + /// + /// Transport method for signed token renewal in CDNI. + /// + /// + /// This claim indicates the transport method to be used for renewing signed tokens in CDNI, + /// ensuring secure and efficient token exchange mechanisms are employed for content delivery + /// and interconnection services. + /// + public const string Cdnistt = "cdnistt"; + + /// + /// Depth of the signed token in CDNI. + /// + /// + /// This claim provides information on the depth or hierarchy level of a signed token within CDNI, + /// potentially influencing content delivery paths or access control decisions based on the token's + /// scope and applicability. + /// + public const string Cdnistd = "cdnistd"; + + #endregion + + #region RFC9321 - Signature Validation + + /// + /// Contains claims used for validating signatures, often in security contexts. + /// + /// + /// This claim set is essential for scenarios where signature validation is critical, providing necessary + /// information to verify the authenticity and integrity of signed data or tokens in secure communications + /// and transactions. + /// + public const string SigValClaims = "sig_val_claims"; + + #endregion + + #region OAuth Rich Authorization Requests (RAR) + + /// + /// JSON array representing the authorization requirements for a specific resource or set of resources. + /// + /// + /// This claim is used in OAuth Rich Authorization Requests (RAR) to specify detailed authorization data + /// for a transaction, enabling fine-grained access control and tailored authorization experiences. + /// + public const string AuthorizationDetails = "authorization_details"; + + #endregion +} diff --git a/Abblix.Jwt/JsonObjectExtensions.cs b/Abblix.Jwt/JsonObjectExtensions.cs new file mode 100644 index 00000000..5a33fa62 --- /dev/null +++ b/Abblix.Jwt/JsonObjectExtensions.cs @@ -0,0 +1,85 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Jwt; + +/// +/// Provides extension methods for the class, enhancing its usability +/// by simplifying the process of accessing and manipulating JSON properties. +/// +/// +/// The extension methods in this class aim to streamline common tasks associated with JSON objects, +/// such as retrieving and setting properties with type safety and minimal boilerplate code. These methods +/// abstract away some of the complexities of working directly with and , +/// offering a more fluent and intuitive interface for developers. +/// +public static class JsonObjectExtensions +{ + /// + /// Retrieves the value of the specified property from a . + /// + /// The instance to extract the property value from. + /// The name of the property whose value is to be retrieved. + /// The expected type of the property value. + /// + /// The value of the specified property if it exists and can be successfully converted to the specified type; + /// otherwise, the default value for the type . + /// + /// + /// This method facilitates the retrieval of typed values from a JSON object, abstracting away the need + /// for manual type checking and conversion. + /// + public static T? GetProperty(this JsonObject json, string name) + { + return json.TryGetPropertyValue(name, out var value) && value != null + ? value.GetValue() + : default; + } + + /// + /// Sets or updates the value of a specified property in a . + /// + /// The instance to modify. + /// The name of the property to set or update. + /// The new value for the property. If null, the property is removed from the . + /// + /// This method provides a convenient way to update the properties of a JSON object, allowing for + /// the addition of new properties or the removal of existing ones by providing a null value. + /// It ensures that the JSON object remains in a consistent state by avoiding the presence of null property values. + /// + public static JsonObject SetProperty(this JsonObject json, string name, JsonNode? value) + { + if (json.TryGetPropertyValue(name, out _)) + { + if (value == null) + json.Remove(name); + } + else if (value != null) + { + json.Add(name, value); + } + + return json; + } +} diff --git a/Abblix.Jwt/JsonWebKey.cs b/Abblix.Jwt/JsonWebKey.cs new file mode 100644 index 00000000..5dccfa5d --- /dev/null +++ b/Abblix.Jwt/JsonWebKey.cs @@ -0,0 +1,199 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; +using Abblix.Utils.Json; + +namespace Abblix.Jwt; + +//TODO consider to split this class into specialized subclasses (RSA/EllipticCurve) +/// +/// Represents a JSON Web Key (JWK), a versatile structure for representing cryptographic keys using JSON. +/// It supports various cryptographic algorithms, including RSA and Elliptic Curve, and can represent both +/// public and private keys. JWKs are crucial for digital signatures, encryption, and ensuring secure +/// communication in web-based protocols. +/// +public record JsonWebKey +{ + /// + /// Identifies the cryptographic algorithm family used with the key, such as RSA or EC (Elliptic Curve), + /// specifying the key's type and its intended cryptographic use. + /// + [JsonPropertyName("kty")] + [JsonPropertyOrder(1)] + public string? KeyType { get; set; } + + /// + /// Indicates the intended use of the key, for example, "sig" (signature) for signing operations or + /// "enc" (encryption) for encryption operations, guiding clients on how to use the key appropriately. + /// + [JsonPropertyName("use")] + [JsonPropertyOrder(2)] + public string? Usage { get; set; } + + /// + /// Specifies the algorithm intended for use with the key, aligning with JWT and JWA specifications + /// to ensure interoperability and secure key management. + /// + [JsonPropertyName("alg")] + [JsonPropertyOrder(3)] + public string? Algorithm { get; set; } + + /// + /// A unique identifier for the key, facilitating key selection and management in multi-key environments, + /// enabling clients and servers to reference and utilize the correct key for cryptographic operations. + /// + [JsonPropertyName("kid")] + [JsonPropertyOrder(4)] + public string? KeyId { get; set; } + + /// + /// Contains a chain of one or more PKIX certificates (RFC 5280), offering a method to associate X.509 + /// certificates with the key for validation and trust chain establishment in secure communications. + /// + [JsonPropertyName("x5c")] + [JsonPropertyOrder(5)] + [JsonConverter(typeof(ArrayConverter))] + public byte[][]? Certificates { get; set; } + + /// + /// A base64url-encoded SHA-1 thumbprint of the DER encoding of an X.509 certificate, providing a compact + /// means to associate a certificate with the JWK for verification purposes without transmitting the full certificate. + /// + [JsonPropertyName("x5t")] + [JsonPropertyOrder(5)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? Thumbprint { get; set; } + + /// + /// RSA Public Key Exponent (e). Part of the RSA public key. + /// + [JsonPropertyName("e")] + [JsonPropertyOrder(6)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? RsaExponent { get; set; } + + /// + /// RSA Public Key Modulus (n). Part of the RSA public key. + /// + [JsonPropertyName("n")] + [JsonPropertyOrder(7)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? RsaModulus { get; set; } + + /// + /// X-coordinate for Elliptic Curve (x). Part of the Elliptic Curve public key. + /// + [JsonPropertyName("x")] + [JsonPropertyOrder(8)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? EllipticalCurvePointX { get; set; } + + /// + /// Y-coordinate for Elliptic Curve (y). Part of the Elliptic Curve public key. + /// + [JsonPropertyName("y")] + [JsonPropertyOrder(9)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? EllipticalCurvePointY { get; set; } + + /// + /// Elliptic Curve Type (crv). Identifies the curve type for an Elliptic Curve key. + /// + [JsonPropertyName("crv")] + [JsonPropertyOrder(10)] + public string? EllipticalCurveType { get; set; } + + /// + /// ECC Private Key or RSA Private Exponent (d). Represents the private part of an RSA or Elliptic Curve key. + /// + [JsonPropertyName("d")] + [JsonPropertyOrder(11)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? PrivateKey { get; set; } + + /// + /// RSA First Prime Factor (p). Part of the RSA private key. + /// + [JsonPropertyName("p")] + [JsonPropertyOrder(12)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? FirstPrimeFactor { get; set; } + + /// + /// RSA Second Prime Factor (q). Part of the RSA private key. + /// + [JsonPropertyName("q")] + [JsonPropertyOrder(13)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? SecondPrimeFactor { get; set; } + + /// + /// RSA First Factor CRT Exponent (dp). Part of the RSA private key in Chinese Remainder Theorem (CRT) format. + /// + [JsonPropertyName("dp")] + [JsonPropertyOrder(14)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? FirstFactorCrtExponent { get; set; } + + /// + /// RSA Second Factor CRT Exponent (dq). Part of the RSA private key in CRT format. + /// + [JsonPropertyName("dq")] + [JsonPropertyOrder(15)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? SecondFactorCrtExponent { get; set; } + + /// + /// RSA First CRT Coefficient (qi). Part of the RSA private key in CRT format. + /// + [JsonPropertyName("qi")] + [JsonPropertyOrder(16)] + [JsonConverter(typeof(Base64UrlTextEncoderConverter))] + public byte[]? FirstCrtCoefficient { get; set; } + + /// + /// Prepares a sanitized version of the JWK that excludes private key information unless explicitly included, + /// suitable for public sharing while preserving the integrity of sensitive data. + /// + /// Whether to include private key data in the sanitized output. + /// + /// A new instance of with or without private key data based on the input parameter. + /// + public JsonWebKey Sanitize(bool includePrivateKeys) + { + return includePrivateKeys switch + { + true when PrivateKey is { Length: > 0} => this, + true => throw new InvalidOperationException($"There is no private key for kid={KeyId}"), + false => this with + { + PrivateKey = null, + FirstCrtCoefficient = null, + FirstPrimeFactor = null, + FirstFactorCrtExponent = null, + SecondPrimeFactor = null, + SecondFactorCrtExponent = null, + } + }; + } +} diff --git a/Abblix.Jwt/JsonWebKeyExtensions.cs b/Abblix.Jwt/JsonWebKeyExtensions.cs new file mode 100644 index 00000000..c9602195 --- /dev/null +++ b/Abblix.Jwt/JsonWebKeyExtensions.cs @@ -0,0 +1,304 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Abblix.Utils; +using Microsoft.IdentityModel.Tokens; + +namespace Abblix.Jwt; + +/// +/// Provides extension methods for the JsonWebKey model to simplify the process of populating its properties from different sources. +/// These methods enable easy conversion between JsonWebKey and various cryptographic representations. +/// +public static class JsonWebKeyExtensions +{ + /// + /// Converts a JsonWebKey to SigningCredentials used in cryptographic operations, specifically for signing tokens. + /// + /// The JsonWebKey to convert. + /// SigningCredentials based on the provided JsonWebKey. + /// Thrown when the algorithm is not supported. + public static SigningCredentials ToSigningCredentials(this JsonWebKey jsonWebKey) + { + return jsonWebKey.Algorithm switch + { + SigningAlgorithms.RS256 => new SigningCredentials(jsonWebKey.ToSecurityKey(), SigningAlgorithms.RS256), + _ => throw new InvalidOperationException($"Not supported algorithm: {jsonWebKey.Algorithm}"), + }; + } + + /// + /// Converts a JsonWebKey to an RsaSecurityKey used in RSA cryptographic operations. + /// + /// The JsonWebKey to convert. + /// RsaSecurityKey based on the provided JsonWebKey. + public static RsaSecurityKey ToSecurityKey(this JsonWebKey jsonWebKey) + { + return new RsaSecurityKey(jsonWebKey.ToRsa()) { KeyId = jsonWebKey.KeyId }; + } + + /// + /// Converts a JsonWebKey to EncryptingCredentials used in cryptographic operations, specifically for encrypting tokens. + /// + /// The JsonWebKey to convert. + /// EncryptingCredentials based on the provided JsonWebKey. + /// Thrown when the algorithm is not supported. + public static EncryptingCredentials ToEncryptingCredentials(this JsonWebKey jsonWebKey) + { + return jsonWebKey.Algorithm switch + { + SigningAlgorithms.RS256 => new EncryptingCredentials( + jsonWebKey.ToSecurityKey(), + SecurityAlgorithms.RsaOAEP, + SecurityAlgorithms.Aes128CbcHmacSha256), + + _ => throw new InvalidOperationException($"Not supported algorithm: {jsonWebKey.Algorithm}"), + }; + } + + /// + /// Converts an X509Certificate2 to a JsonWebKey. The private keys can be optionally included in the conversion. + /// + /// The X509Certificate2 to convert. + /// Indicates whether to include private keys in the conversion. + /// A JsonWebKey representing the certificate. + public static JsonWebKey ToJsonWebKey(this X509Certificate2 certificate, bool includePrivateKeys = false) + => new X509SecurityKey(certificate).ToJsonWebKey(SecurityAlgorithms.RsaSha256, includePrivateKeys); + + /// + /// Converts a SecurityKey to a JsonWebKey with a specified algorithm. The private keys can be optionally included. + /// + /// The SecurityKey to convert. + /// The algorithm to be used with the key. + /// Indicates whether to include private keys in the conversion. + /// A JsonWebKey representing the SecurityKey. + public static JsonWebKey ToJsonWebKey(this SecurityKey key, string algorithm, bool includePrivateKeys = false) + { + // TODO Move it to a virtual method of generic Credentials class with specific implementations in derived classes + var jwk = new JsonWebKey + { + Usage = key.MapKeyUsage(), + Algorithm = AlgorithmMapper.MapToOutbound(algorithm), + KeyId = key.KeyId, + }; + + return key switch + { + X509SecurityKey { PublicKey: RSA publicKey, PrivateKey: RSA privateKey, Certificate: { } cert } + => jwk + .Apply(publicKey.ExportParameters(false)) + .Apply(privateKey.ExportParameters(includePrivateKeys)) + .Apply(cert), + + X509SecurityKey { PublicKey: RSA publicKey, Certificate: { } cert } when !includePrivateKeys + => jwk + .Apply(publicKey.ExportParameters(false)) + .Apply(cert), + + X509SecurityKey { PublicKey: ECDsa publicKey, PrivateKey: ECDsa privateKey, Certificate: { } cert } + => jwk + .Apply(publicKey.ExportParameters(false)) + .Apply(privateKey.ExportParameters(includePrivateKeys)) + .Apply(cert), + + X509SecurityKey { PublicKey: ECDsa publicKey, Certificate: { } cert } when !includePrivateKeys + => jwk + .Apply(publicKey.ExportParameters(false)) + .Apply(cert), + + RsaSecurityKey { Rsa: { } rsa } => jwk.Apply(rsa.ExportParameters(includePrivateKeys)), + RsaSecurityKey { Parameters: var parameters } => jwk.Apply(parameters), + + ECDsaSecurityKey { ECDsa: { } ecDsa } => jwk.Apply(ecDsa.ExportParameters(includePrivateKeys)), + + Microsoft.IdentityModel.Tokens.JsonWebKey jsonWebKey => jwk.Apply(jsonWebKey, includePrivateKeys), + + _ => throw new InvalidOperationException($"The key type {key.GetType().FullName} is not supported"), + }; + } + + /// + /// Maps the key usage of a SecurityKey to a string representation. + /// Determines whether the key is used for signing, encryption, or both. + /// + /// The SecurityKey whose usage is to be determined. + /// A string representing the usage of the key. + private static string MapKeyUsage(this SecurityKey key) + { + const string defaultUsage = PublicKeyUsages.Signature; + + if (key is not X509SecurityKey x509Key) + return defaultUsage; + + var keyUsage = x509Key.Certificate.Extensions.OfType().FirstOrDefault(); + if (keyUsage == null) + return defaultUsage; + + var sig = keyUsage.KeyUsages.HasFlag(X509KeyUsageFlags.DigitalSignature); + var enc = keyUsage.KeyUsages.HasFlag(X509KeyUsageFlags.KeyEncipherment | + X509KeyUsageFlags.DataEncipherment); + return (sig, enc) switch + { + (true, true) => PublicKeyUsages.Signature + " " + PublicKeyUsages.Encryption, + (true, false) => PublicKeyUsages.Signature, + (false, true) => PublicKeyUsages.Encryption, + _ => defaultUsage, + }; + } + + /// + /// Applies X509Certificate2 properties to a JsonWebKey. + /// + /// The JsonWebKey to which the certificate properties are to be applied. + /// The X509Certificate2 providing the properties. + /// The updated JsonWebKey with applied certificate properties. + public static JsonWebKey Apply(this JsonWebKey jwk, X509Certificate2 certificate) + { + jwk.Certificates = new[] { certificate.RawData }; + jwk.Thumbprint = certificate.GetCertHash(); + return jwk; + } + + /// + /// Applies RSA parameters to a JsonWebKey. + /// + /// The JsonWebKey to which the RSA parameters are to be applied. + /// The RSAParameters providing the RSA key information. + /// The updated JsonWebKey with applied RSA parameters. + public static JsonWebKey Apply(this JsonWebKey jwk, RSAParameters parameters) + { + jwk.KeyType = JsonWebKeyTypes.Rsa; + jwk.RsaExponent = parameters.Exponent; + jwk.RsaModulus = parameters.Modulus; + + jwk.PrivateKey = parameters.D; + jwk.FirstPrimeFactor = parameters.P; + jwk.SecondPrimeFactor = parameters.Q; + jwk.FirstFactorCrtExponent = parameters.DP; + jwk.SecondFactorCrtExponent = parameters.DQ; + jwk.FirstCrtCoefficient = parameters.InverseQ; + + return jwk; + } + + private static class EllipticalCurveOids + { + public const string P256 = "1.2.840.10045.3.1.7"; + public const string P384 = "1.3.132.0.34"; + public const string P521 = "1.3.132.0.35"; + } + + /// + /// Applies Elliptic Curve parameters to a JsonWebKey. + /// + /// The JsonWebKey to which the EC parameters are to be applied. + /// The ECParameters providing the Elliptic Curve key information. + /// The updated JsonWebKey with applied Elliptic Curve parameters. + private static JsonWebKey Apply(this JsonWebKey jwk, ECParameters parameters) + { + jwk.KeyType = JsonWebKeyTypes.EllipticalCurve; + + var curveOid = parameters.Curve.Oid; + jwk.EllipticalCurveType = curveOid.Value switch + { + EllipticalCurveOids.P256 => JsonWebKeyECTypes.P256, + EllipticalCurveOids.P384 => JsonWebKeyECTypes.P384, + EllipticalCurveOids.P521 => JsonWebKeyECTypes.P521, + _ => throw new InvalidOperationException($"The OID [{curveOid.Value}] {curveOid.FriendlyName} is not supported"), + }; + + jwk.EllipticalCurvePointX = parameters.Q.X; + jwk.EllipticalCurvePointY = parameters.Q.Y; + + if (parameters.D != null) jwk.PrivateKey = parameters.D; + + return jwk; + } + + /// + /// Converts a Microsoft.IdentityModel.Tokens.JsonWebKey to a custom JsonWebKey. + /// This method allows the conversion of keys between different JsonWebKey implementations. + /// + /// The JsonWebKey to be converted. + /// The Microsoft.IdentityModel.Tokens.JsonWebKey to convert. + /// Indicates whether to include private keys in the conversion. + /// A JsonWebKey representing the Microsoft.IdentityModel.Tokens.JsonWebKey. + private static JsonWebKey Apply(this JsonWebKey jwk, Microsoft.IdentityModel.Tokens.JsonWebKey key, bool includePrivateKeys = false) + { + jwk.KeyType = key.Kty; + jwk.Usage = key.Use ?? PublicKeyUsages.Signature; + jwk.Algorithm = key.Alg; + jwk.KeyId = key.Kid; + + jwk.Certificates = key.X5c is { Count: > 0 } certificates ? certificates.Select(HttpServerUtility.UrlTokenDecode).ToArray() : null; + jwk.Thumbprint = HttpServerUtility.UrlTokenDecode(key.X5t); + + jwk.RsaExponent = HttpServerUtility.UrlTokenDecode(key.E); + jwk.RsaModulus = HttpServerUtility.UrlTokenDecode(key.N); + + jwk.EllipticalCurveType = key.Crv; + jwk.EllipticalCurvePointX = HttpServerUtility.UrlTokenDecode(key.X); + jwk.EllipticalCurvePointY = HttpServerUtility.UrlTokenDecode(key.Y); + + if (includePrivateKeys) + { + jwk.PrivateKey = HttpServerUtility.UrlTokenDecode(key.D); + jwk.FirstPrimeFactor = HttpServerUtility.UrlTokenDecode(key.P); + jwk.SecondPrimeFactor = HttpServerUtility.UrlTokenDecode(key.Q); + jwk.FirstFactorCrtExponent = HttpServerUtility.UrlTokenDecode(key.DP); + jwk.SecondFactorCrtExponent = HttpServerUtility.UrlTokenDecode(key.DQ); + jwk.FirstCrtCoefficient = HttpServerUtility.UrlTokenDecode(key.QI); + } + return jwk; + } + + /// + /// Converts a JsonWebKey to an RSA object, which represents an RSA public and private key pair or just a public key. + /// + /// The JsonWebKey to be converted. + /// An RSA object based on the provided JsonWebKey. + public static RSA ToRsa(this JsonWebKey key) + { + var rsa = RSA.Create(); + rsa.ImportParameters(key.ToRsaParameters()); + return rsa; + } + + /// + /// Converts a JsonWebKey to RSAParameters, which represent the key parameters used in RSA cryptographic operations. + /// + /// The JsonWebKey to be converted. + /// An RSAParameters object based on the provided JsonWebKey. + public static RSAParameters ToRsaParameters(this JsonWebKey key) => new() + { + Modulus = key.RsaModulus, + Exponent = key.RsaExponent, + D = key.PrivateKey, + P = key.FirstPrimeFactor, + Q = key.SecondPrimeFactor, + DP = key.FirstFactorCrtExponent, + DQ = key.SecondFactorCrtExponent, + InverseQ = key.FirstCrtCoefficient, + }; +} diff --git a/Abblix.Jwt/JsonWebKeyFactory.cs b/Abblix.Jwt/JsonWebKeyFactory.cs new file mode 100644 index 00000000..1ee7b019 --- /dev/null +++ b/Abblix.Jwt/JsonWebKeyFactory.cs @@ -0,0 +1,73 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using Microsoft.IdentityModel.Tokens; + +namespace Abblix.Jwt; + +/// +/// A factory for creating JsonWebKey objects, focusing on RSA keys. +/// This class provides a method to generate RSA keys with a specified usage and key size, +/// which can be used for signing or encryption in cryptographic operations. +/// +public static class JsonWebKeyFactory +{ + /// + /// Creates an RSA JsonWebKey with a specified usage and key size. + /// + /// The intended usage of the key, typically 'sig' for signing or 'enc' for encryption. + /// The size of the RSA key in bits. The default is 2048 bits, which is commonly used and + /// provides a good security level. + /// A that contains the RSA key details suitable for JWT operations. + public static JsonWebKey CreateRsa(string usage, int keySize = 2048 /* Recommended key size for RSA */) + { + var algorithm = usage switch + { + JsonWebKeyUseNames.Sig or JsonWebKeyUseNames.Enc => "RS256", + _ => throw new ArgumentException( + $"Invalid usage specified. Valid options are '{JsonWebKeyUseNames.Sig}' for signing or '{JsonWebKeyUseNames.Enc}' for encryption.", + nameof(usage)) + }; + + using var rsa = RSA.Create(); + rsa.KeySize = keySize; + var parameters = rsa.ExportParameters(true); + + var key = new JsonWebKey + { + KeyType = "RSA", + Algorithm = algorithm, + Usage = usage, + RsaExponent = parameters.Exponent, + RsaModulus = parameters.Modulus, + PrivateKey = parameters.D, + FirstPrimeFactor = parameters.P, + SecondPrimeFactor = parameters.Q, + FirstFactorCrtExponent = parameters.DP, + SecondFactorCrtExponent = parameters.DQ, + FirstCrtCoefficient = parameters.InverseQ, + }; + + return key; + } +} diff --git a/Abblix.Jwt/JsonWebKeySet.cs b/Abblix.Jwt/JsonWebKeySet.cs new file mode 100644 index 00000000..b722ace4 --- /dev/null +++ b/Abblix.Jwt/JsonWebKeySet.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Jwt; + +/// +/// Represents a JSON Web Key Set (JWKS) as defined by the JSON Web Key (JWK) specifications. +/// A JWKS is a set of keys containing the public keys used to verify any JSON Web Token (JWT) issued by the authorization server. +/// +public record JsonWebKeySet(JsonWebKey[] Keys) +{ + /// + /// Gets an array of objects representing the cryptographic keys. + /// + /// + /// The 'keys' property in a JWKS is an array of JWK objects. Each JWK object within the array is a JSON object representing a single public key. + /// + [JsonPropertyName("keys")] public JsonWebKey[] Keys { get; init; } = Keys; +} diff --git a/Abblix.Jwt/JsonWebKeyTypes.cs b/Abblix.Jwt/JsonWebKeyTypes.cs new file mode 100644 index 00000000..50f8893d --- /dev/null +++ b/Abblix.Jwt/JsonWebKeyTypes.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Provides constants for the types of JSON Web Keys (JWK) as defined in the JWK specifications. +/// These constants are used to identify the cryptographic algorithm family used by the key. +/// +public static class JsonWebKeyTypes +{ + /// + /// Represents an Elliptical Curve cryptographic key. + /// This type is used for keys that employ elliptic curve cryptography (ECC) algorithms. + /// + public const string EllipticalCurve = "EC"; + + /// + /// Represents a RSA cryptographic key. + /// This type is used for keys that employ RSA cryptography algorithms. + /// + public const string Rsa = "RSA"; +} diff --git a/Abblix.Jwt/JsonWebToken.cs b/Abblix.Jwt/JsonWebToken.cs new file mode 100644 index 00000000..2ca56edd --- /dev/null +++ b/Abblix.Jwt/JsonWebToken.cs @@ -0,0 +1,53 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Jwt; + +/// +/// Represents a JSON Web Token (JWT), a compact, URL-safe means of representing +/// claims to be transferred between two parties. This record encapsulates the standard +/// JWT structure, offering properties to access and manipulate the header, payload, and claims. +/// +public record JsonWebToken +{ + /// + /// Represents the JWT header, containing metadata about the type of token and the algorithms used to secure it. + /// + /// + /// The header typically includes information such as the type of token (JWT) and + /// the signing algorithm (e.g., HS256, RS256). This property allows direct access + /// and manipulation of these values. + /// + public JsonWebTokenHeader Header { get; init; } = new(new JsonObject()); + + /// + /// Represents the JWT payload, containing the claims about the entity (typically, the user) + /// and additional metadata. + /// + /// + /// The payload is where the claims of the JWT are stored. This includes standard claims such as + /// issuer, subject, and expiration time, as well as custom claims as required by the application. + /// + public JsonWebTokenPayload Payload { get; init; } = new(new JsonObject()); +} diff --git a/Abblix.Jwt/JsonWebTokenAlgorithms.cs b/Abblix.Jwt/JsonWebTokenAlgorithms.cs new file mode 100644 index 00000000..e863372c --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenAlgorithms.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.IdentityModel.Tokens.Jwt; + +namespace Abblix.Jwt; + +internal static class JsonWebTokenAlgorithms +{ + /// /// + /// Defines a static collection of signing algorithm values that are supported for JSON Web Tokens (JWTs). + /// This collection is based on the algorithms recognized by the JwtSecurityTokenHandler for outbound tokens, + /// including an option for 'none' to allow unsigned tokens. This class serves as a central metadata repository + /// for supported algorithms used by both JsonWebTokenCreator and JsonWebTokenValidator classes, + /// leveraging JwtSecurityTokenHandler under the hood. + /// + public static readonly IEnumerable SigningAlgValuesSupported = + JwtSecurityTokenHandler.DefaultOutboundAlgorithmMap.Values + .Append(SigningAlgorithms.None) + .ToArray(); +} diff --git a/Abblix.Jwt/JsonWebTokenCreator.cs b/Abblix.Jwt/JsonWebTokenCreator.cs new file mode 100644 index 00000000..ab173ab6 --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenCreator.cs @@ -0,0 +1,136 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.IdentityModel.Tokens.Jwt; +using Microsoft.IdentityModel.Tokens; + +namespace Abblix.Jwt; + +/// +/// Responsible for creating JSON Web Tokens (JWTs). This class provides functionality to issue JWTs based on given claims and keys. +/// +/// +/// The class uses to define the token's properties and leverages +/// for token creation. It supports setting standard claims such as issuer, audience, and times, as well as custom claims +/// contained within the provided instance. +/// +public sealed class JsonWebTokenCreator : IJsonWebTokenCreator +{ + /// + /// Gets the collection of signing algorithms supported for JWT creation. + /// This property reflects the JWT security token handler's default outbound algorithm mapping, + /// indicating the algorithms available for signing the tokens. + /// + public IEnumerable SigningAlgValuesSupported => JsonWebTokenAlgorithms.SigningAlgValuesSupported; + + /// + /// Asynchronously issues a JWT based on the specified JsonWebToken, signing key, and optional encrypting key. + /// + /// The JsonWebToken object containing the payload of the JWT. + /// The signing key as a JsonWebKey to sign the JWT. + /// Optional encrypting key as a JsonWebKey to encrypt the JWT. + /// A Task that represents the asynchronous operation and yields the JWT as a string. + /// Thrown if the specified date time values cause an overflow. + /// + /// The method configures a based on the provided JWT, signing key, and encrypting key. + /// It then creates the JWT using and returns the serialized token string. + /// Note: The audience is set to the first value if multiple audiences are provided, due to limitations of + /// . + /// + public Task IssueAsync( + JsonWebToken jwt, + JsonWebKey? signingKey, + JsonWebKey? encryptingKey = null) + { + var descriptor = new SecurityTokenDescriptor + { + TokenType = jwt.Header.Type, + Issuer = jwt.Payload.Issuer, + Audience = jwt.Payload.Audiences?.SingleOrDefault(), //TODO replace JwtSecurityTokenHandler with own code to overcome this limitation + + IssuedAt = CheckDateOverflow(jwt.Payload.IssuedAt, nameof(jwt.Payload.IssuedAt)), + NotBefore = CheckDateOverflow(jwt.Payload.NotBefore, nameof(jwt.Payload.NotBefore)), + Expires = CheckDateOverflow(jwt.Payload.ExpiresAt, nameof(jwt.Payload.ExpiresAt)), + + Claims = Convert(jwt.Payload), + }; + + if (signingKey != null) + descriptor.SigningCredentials = signingKey.ToSigningCredentials(); + + if (encryptingKey != null) + descriptor.EncryptingCredentials = encryptingKey.ToEncryptingCredentials(); + + var token = new JwtSecurityTokenHandler().CreateJwtSecurityToken(descriptor); + + return Task.FromResult(token.RawData); + } + + /// + /// Checks if the specified DateTimeOffset is within the allowable range for JWT date/time claims. + /// + /// The DateTimeOffset to check. + /// The name of the date/time claim for error reporting purposes. + /// A DateTime representation of the DateTimeOffset if it is within the allowable range. + /// Thrown if the specified DateTimeOffset causes an overflow. + /// + /// This method ensures that date/time claims do not exceed the maximum possible value that can be encoded in a JWT, + /// preventing potential issues with token processing and validation. + /// + private static DateTime? CheckDateOverflow(DateTimeOffset? dateTime, string name) + { + if (!dateTime.HasValue) + return null; + + var maxPossibleDateTime = DateTimeOffset.UnixEpoch.AddSeconds(int.MaxValue); + if (maxPossibleDateTime < dateTime) + throw new ArgumentOutOfRangeException(name, dateTime, $"{name} value causes overflow: {dateTime}"); + + return dateTime.Value.UtcDateTime; + } + + /// + /// Converts a collection of JwtClaims into a dictionary suitable for a JWT payload. + /// + /// The JsonWebTokenPayload containing the claims to be converted. + /// A dictionary of claims where the key is the claim type and the value is the claim value. + /// + /// This method facilitates the conversion of the JWT payload into a format compatible with . + /// It ensures that each claim is correctly represented, handling cases where claims have single or multiple values. + /// + private static IDictionary Convert(JsonWebTokenPayload payload) + { + var uniqueClaims = payload.Json + .ExceptBy( + JwtSecurityTokenHandlerConstants.ClaimTypesToExclude, + claim => claim.Key) + .GroupBy( + claim => claim.Key, + claim => claim.Value.ToJsonElement()); + + var result = uniqueClaims.ToDictionary( + claim => claim.Key, + claim => claim.Count() == 1 ? (object)claim.Single() : claim.ToArray()); + + return result; + } +} diff --git a/Abblix.Jwt/JsonWebTokenExtensions.cs b/Abblix.Jwt/JsonWebTokenExtensions.cs new file mode 100644 index 00000000..037e36ed --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenExtensions.cs @@ -0,0 +1,225 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Nodes; +using Abblix.Utils; + +namespace Abblix.Jwt; + +/// +/// Provides extension methods for handling JSON data within JWTs. +/// +public static class JsonWebTokenExtensions +{ + /// + /// Retrieves a value from a based on + /// a property stored as Unix time seconds. + /// + /// The from which to retrieve the date/time value. + /// The property name containing the Unix time seconds. + /// + /// A nullable representing the date and time of the specified property, + /// or null if the property is not present or cannot be converted. + /// + /// + /// Unix time seconds are widely used for representing date and time in JSON objects, especially in JWTs. + /// This method simplifies retrieving such values by converting them directly to . + /// + public static DateTimeOffset? GetUnixTimeSeconds(this JsonObject json, string name) + { + var node = json[name]; + if (node == null) + return null; + + var value = node.AsValue(); + var seconds = value.TryGetValue(out var intValue) ? intValue : value.GetValue(); + return DateTimeOffset.FromUnixTimeSeconds(seconds); + } + + /// + /// Sets a value in a , stored as Unix time seconds. + /// + /// The to modify. + /// The property name under which to store the Unix time seconds. + /// The value to set. If null, + /// the property will be removed from the JSON object. + /// The modified . + /// + /// Storing dates as Unix time seconds is a common practice in JWTs and other JSON structures. + /// This method facilitates setting such values by converting to Unix time seconds. + /// + public static void SetUnixTimeSeconds(this JsonObject json, string name, DateTimeOffset? value) + { + var jsonValue = value.HasValue ? JsonValue.Create(value.Value.ToUnixTimeSeconds()) : null; + json.SetProperty(name, jsonValue); + } + + /// + /// Retrieves an array of strings from a based on a specified property name. + /// This method supports both single string values and arrays of strings. + /// + /// The from which to retrieve the array of strings. + /// The property name to retrieve the values from. + /// An enumerable of strings if the property exists; otherwise, an empty enumerable. + /// + /// This method is useful for JWT claims or other JSON structures where a property may contain either + /// a single string value or an array of strings. + /// + public static IEnumerable GetArrayOfStrings(this JsonObject json, string name) + { + if (!json.TryGetPropertyValue(name, out var jsonNode)) + yield break; + + switch (jsonNode) + { + case null: + break; + + case JsonValue value: + yield return value.GetValue(); + break; + + case JsonArray array: + foreach (var element in array) + if (element != null) + yield return element.GetValue(); + break; + } + } + + /// + /// Sets a property in a with a value that can be either a single string or an array of strings, + /// depending on the number of items in the provided enumerable. + /// + /// The to modify. + /// The name of the property to set. + /// The enumerable of string values to set as the property's value. + /// + /// This method is versatile for JWT or JSON handling where a property may accept both single and multiple values. + /// + public static void SetArrayOrString(this JsonObject json, string name, IEnumerable values) + { + json.SetProperty(name, values.ToJsonNode()); + } + + /// + /// Converts a collection of strings to a JSON node, which will be either a single JSON value if there's only one string, + /// or a JSON array if there are multiple strings. + /// + /// The collection of strings to convert. + /// A representing either a single value or an array of strings, or null if the collection is empty. + private static JsonNode? ToJsonNode(this IEnumerable values) + { + using var enumerator = values.GetEnumerator(); + + if (!enumerator.MoveNext()) + return null; + + var firstValue = enumerator.Current; + + if (!enumerator.MoveNext()) + return JsonValue.Create(firstValue); + + var array = new JsonArray { firstValue }; + + do array.Add(enumerator.Current); + while (enumerator.MoveNext()); + + return array; + } + + /// + /// Retrieves a collection of strings from a space-separated string stored in a specified property of a . + /// + /// The from which to retrieve the space-separated strings. + /// The name of the property containing the space-separated string. + /// An enumerable of strings if the property exists and contains values; otherwise, an empty enumerable. + /// + /// This method simplifies extracting multiple values from a single string property, common in JWT and OAuth scenarios. + /// + public static IEnumerable GetSpaceSeparatedStrings(this JsonObject json, string name) + { + var values = json.GetProperty(name); + return values.HasValue() + ? values.Split(' ', StringSplitOptions.RemoveEmptyEntries) + : Enumerable.Empty(); + } + + /// + /// Sets a property in a with a value represented as a space-separated string from an enumerable of strings. + /// + /// The to modify. + /// The name of the property to set. + /// The enumerable of string values to join into a space-separated string. + /// The modified . + /// + /// This method is useful for setting JWT claims or other JSON properties that accept a list of values as a single space-separated string. + /// + public static void SetSpaceSeparatedStrings(this JsonObject json, string name, IEnumerable value) + { + json.SetProperty(name, string.Join(' ', value)); + } + + /// + /// A static representing a null value in JSON. + /// This is used as a default value when a null JSON node needs to be represented as a . + /// + private static readonly JsonElement NullJsonElement = "null".ToJsonElement(); + + /// + /// Converts a JsonNode to a JsonElement. + /// + /// The JsonNode to convert. + /// The converted JsonElement. + public static JsonElement ToJsonElement(this JsonNode? jsonNode) + { + return jsonNode == null ? NullJsonElement : jsonNode.ToJsonString().ToJsonElement(); + } + + /// + /// Converts a JSON string to a . + /// + /// The JSON string to convert. + /// A representing the parsed JSON structure. + /// + /// Thrown when the JSON string is malformed and cannot be parsed. + /// + /// + /// This method is useful for converting a JSON string into a , + /// allowing for easy manipulation and traversal of the JSON structure. + /// + private static JsonElement ToJsonElement(this string jsonString) + => JsonDocument.Parse(jsonString).RootElement; + + /// + /// Converts a JsonElement to a JsonNode, allowing for more dynamic manipulation of the JSON structure. + /// + /// The JsonElement to convert. + /// The converted JsonNode. + /// + /// This method is useful when you need to convert from a structured JsonElement to a more flexible JsonNode. + /// + public static JsonNode? ToJsonNode(this JsonElement jsonElement) + => JsonNode.Parse(jsonElement.GetRawText()); + +} diff --git a/Abblix.Jwt/JsonWebTokenHeader.cs b/Abblix.Jwt/JsonWebTokenHeader.cs new file mode 100644 index 00000000..4434c1b3 --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenHeader.cs @@ -0,0 +1,78 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Jwt; + +/// +/// Represents the header part of a JSON Web Token (JWT), containing metadata about the token +/// such as the type and the algorithm used for signing. +/// +/// +/// The JWT header typically specifies the cryptographic operations applied to the JWT +/// and can also include additional properties defined or required by the application. +/// +public class JsonWebTokenHeader +{ + /// + /// Initializes a new instance of the class with the specified JSON object. + /// + /// The representing the JWT header. + public JsonWebTokenHeader(JsonObject json) + { + Json = json; + } + + /// + /// The underlying JSON object representing the JWT header. + /// + public JsonObject Json { get; } + + + /// + /// Gets or sets the type of the JWT, typically "JWT" or a similar identifier. + /// This field is optional and used to declare the media type of the JWT. + /// + /// + /// The 'typ' parameter is recommended when the JWT is embedded in places not inherently + /// carrying this information, helping recipients process the JWT type accordingly. + /// + public string? Type + { + get => Json.GetProperty(JwtClaimTypes.Type); + set => Json.SetProperty(JwtClaimTypes.Type, value); + } + + /// + /// Gets or sets the algorithm used to sign the JWT, indicating how the token is secured. + /// + /// + /// The 'alg' parameter identifies the cryptographic algorithm used to secure the JWT. + /// Common algorithms include HS256, RS256, and ES256. It is crucial for verifying the JWT's integrity. + /// + public string? Algorithm + { + get => Json.GetProperty(JwtClaimTypes.Algorithm); + set => Json.SetProperty(JwtClaimTypes.Algorithm, value); + } +} diff --git a/Abblix.Jwt/JsonWebTokenPayload.cs b/Abblix.Jwt/JsonWebTokenPayload.cs new file mode 100644 index 00000000..c556cfb9 --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenPayload.cs @@ -0,0 +1,204 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Jwt; + +/// +/// Represents the payload part of a JSON Web Token (JWT), containing the claims or statements about the subject. +/// +/// +/// The JWT payload is a JSON object that contains the claims transmitted by the token. Standard claims +/// such as issuer, subject, expiration time, and more can be included, as well as additional claims as needed. +/// This class provides a convenient way to work with the payload, allowing for easy access and modification of claims. +/// +public class JsonWebTokenPayload +{ + /// + /// Initializes a new instance of the class with the specified JSON object. + /// + /// The representing the JWT payload. + public JsonWebTokenPayload(JsonObject json) + { + Json = json; + } + + /// + /// The underlying JSON object representing the JWT payload. + /// + public JsonObject Json { get; } + + /// + /// Indexer to get or set claim values in the payload using the claim name. + /// + /// The name of the claim. + /// The value of the claim if it exists; otherwise, null. + public JsonNode? this[string name] { + get => Json[name]; + set => Json.SetProperty(name, value); + } + + /// + /// The unique identifier of the JWT. + /// + public string? JwtId + { + get => Json.GetProperty(JwtClaimTypes.JwtId); + set => Json.SetProperty(JwtClaimTypes.JwtId, value); + } + + /// + /// The time at which the JWT was issued, represented as a Unix timestamp. + /// + public DateTimeOffset? IssuedAt + { + get => Json.GetUnixTimeSeconds(JwtClaimTypes.IssuedAt); + set => Json.SetUnixTimeSeconds(JwtClaimTypes.IssuedAt, value); + } + + /// + /// The time before which the JWT must not be accepted for processing, represented as a Unix timestamp. + /// + public DateTimeOffset? NotBefore + { + get => Json.GetUnixTimeSeconds(JwtClaimTypes.NotBefore); + set => Json.SetUnixTimeSeconds(JwtClaimTypes.NotBefore, value); + } + + /// + /// The expiration time on or after which the JWT must not be accepted for processing, represented as a Unix timestamp. + /// + public DateTimeOffset? ExpiresAt + { + get => Json.GetUnixTimeSeconds(JwtClaimTypes.ExpiresAt); + set => Json.SetUnixTimeSeconds(JwtClaimTypes.ExpiresAt, value); + } + + /// + /// The issuer of the JWT. + /// + public string? Issuer + { + get => Json.GetProperty(JwtClaimTypes.Issuer); + set => Json.SetProperty(JwtClaimTypes.Issuer, value); + } + + /// + /// The intended audiences for the JWT. + /// + public IEnumerable Audiences + { + get => Json.GetArrayOfStrings(JwtClaimTypes.Audience); + set => Json.SetArrayOrString(JwtClaimTypes.Audience, value); + } + + /// + /// Gets or sets the subject of the JWT. + /// The subject typically represents the principal that is the focus of the JWT, often a user identifier. + /// + /// + /// The 'sub' (subject) claim is a standard claim in JWTs used to uniquely identify the principal, + /// usually in the context of authentication or user identity. It is commonly a user ID or username. + /// + public string? Subject + { + get => Json.GetProperty(JwtClaimTypes.Subject); + set => Json.SetProperty(JwtClaimTypes.Subject, value); + } + + /// + /// The session ID associated with the JWT, typically used to manage session state across applications. + /// + /// + /// The session ID can link the JWT to a specific session for the user, allowing for effective session management and security controls. + /// + public string? SessionId + { + get => Json.GetProperty(JwtClaimTypes.SessionId); + set => Json.SetProperty(JwtClaimTypes.SessionId, value); + } + + /// + /// The client ID for which the JWT was issued, identifying the client application in OAuth 2.0 and OpenID Connect flows. + /// + /// + /// This property is crucial in scenarios where the JWT is used to convey or assert the identity of a client application to the authorization server or resource server. + /// + public string? ClientId + { + get => Json.GetProperty(JwtClaimTypes.ClientId); + set => Json.SetProperty(JwtClaimTypes.ClientId, value); + } + + /// + /// The scope of access granted by the JWT. + /// Scope is typically a space-separated list of permissions or access levels and is not part of the standard JWT claims. + /// + /// + /// The 'scope' claim is often used in OAuth 2.0 and OpenID Connect contexts to specify the extent of access + /// granted by the token. Each value in the list represents a specific permission or access level granted to the token bearer. + /// This property ensures that the scope is represented appropriately as either a single value or an array of values. + /// + public IEnumerable Scope + { + get => Json.GetSpaceSeparatedStrings(JwtClaimTypes.Scope); + set => Json.SetSpaceSeparatedStrings(JwtClaimTypes.Scope, value); + } + + /// + /// Identifies the identity provider that authenticated the end user, useful in federated identity scenarios. + /// + /// + /// This claim is particularly relevant in systems that support multiple identity providers, + /// helping to trace the origin of the authentication and ensuring that the JWT can be validated appropriately. + /// + public string? IdentityProvider + { + get => Json.GetProperty(JwtClaimTypes.IdentityProvider); + set => Json.SetProperty(JwtClaimTypes.IdentityProvider, value); + } + + /// + /// Represents the time when the authentication occurred, facilitating checks against token freshness + /// and replay attacks. + /// + /// + /// Storing the authentication time is critical for applications requiring a high level of assurance + /// regarding the moment a user was authenticated, allowing for precise control over session validity + /// and user authentication status. + /// + public DateTimeOffset? AuthenticationTime + { + get => Json.GetUnixTimeSeconds(JwtClaimTypes.AuthenticationTime); + set => Json.SetUnixTimeSeconds(JwtClaimTypes.AuthenticationTime, value); + } + + /// + /// A value used to associate a client session with an ID token, mitigating replay attacks. + /// + public string? Nonce + { + get => Json.GetProperty(JwtClaimTypes.Nonce); + set => Json.SetProperty(JwtClaimTypes.Nonce, value); + } +} diff --git a/Abblix.Jwt/JsonWebTokenValidator.cs b/Abblix.Jwt/JsonWebTokenValidator.cs new file mode 100644 index 00000000..712e4763 --- /dev/null +++ b/Abblix.Jwt/JsonWebTokenValidator.cs @@ -0,0 +1,175 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text.Json.Nodes; +using Abblix.Utils; +using Microsoft.IdentityModel.Tokens; + +namespace Abblix.Jwt; + +/// +/// Represents a validator for JSON Web Tokens (JWTs) which validates a JWT against specified validation parameters. +/// +public class JsonWebTokenValidator : IJsonWebTokenValidator +{ + /// + /// Provides a collection of signing algorithms supported by the validator. This includes all algorithms recognized + /// by the JwtSecurityTokenHandler for inbound tokens, as well as an option to accept tokens without a signature. + /// This allows for flexibility in validating JWTs with various security requirements. + /// + public IEnumerable SigningAlgValuesSupported => JsonWebTokenAlgorithms.SigningAlgValuesSupported; + + /// + /// Asynchronously validates a JWT string against specified validation parameters. + /// + /// The JWT string to validate. + /// The parameters defining the validation rules and requirements. + /// A task representing the validation operation, with a result of JwtValidationResult indicating the validation outcome. + public Task ValidateAsync(string jwt, ValidationParameters parameters) + => Task.FromResult(Validate(jwt, parameters)); + + /// + /// Performs the actual validation of a JWT string based on the specified validation parameters. + /// + /// The JWT string to validate. + /// The validation parameters. + /// The result of the JWT validation process, either indicating success or detailing any validation errors. + private static JwtValidationResult Validate(string jwt, ValidationParameters parameters) + { + var tokenValidationParameters = new TokenValidationParameters + { + NameClaimType = JwtClaimTypes.Subject, + + ValidateIssuer = parameters.Options.HasFlag(ValidationOptions.ValidateIssuer), + ValidateAudience = parameters.Options.HasFlag(ValidationOptions.ValidateAudience), + RequireSignedTokens = parameters.Options.HasFlag(ValidationOptions.RequireSignedTokens), + ValidateIssuerSigningKey = parameters.Options.HasFlag(ValidationOptions.ValidateIssuerSigningKey), + ValidateLifetime = parameters.Options.HasFlag(ValidationOptions.ValidateLifetime), + }; + + if (tokenValidationParameters.ValidateIssuer) + { + var validateIssuer = parameters.ValidateIssuer + .NotNull(nameof(parameters.ValidateIssuer)); + + tokenValidationParameters.IssuerValidator = (issuer, _, _) => + validateIssuer(issuer).Result ? issuer : null; + } + + if (tokenValidationParameters.ValidateAudience) + { + var validateAudience = parameters.ValidateAudience + .NotNull(nameof(parameters.ValidateAudience)); + + tokenValidationParameters.AudienceValidator = (audiences, _, _) => + validateAudience(audiences).Result; + } + + if (tokenValidationParameters.ValidateIssuerSigningKey) + { + var resolveIssuerSigningKeys = parameters.ResolveIssuerSigningKeys + .NotNull(nameof(parameters.ResolveIssuerSigningKeys)); + + tokenValidationParameters.IssuerSigningKeyResolver = (_, securityToken, keyId, _) => + { + var signingKeys = resolveIssuerSigningKeys(securityToken.Issuer); + + if (keyId.HasValue()) + signingKeys = signingKeys.WhereAsync(key => key.KeyId == keyId); + + return signingKeys.SelectAsync(key => key.ToSecurityKey()).ToListAsync().Result; + }; + } + + var resolveTokenDecryptionKeys = parameters.ResolveTokenDecryptionKeys; + if (resolveTokenDecryptionKeys != null) + tokenValidationParameters.TokenDecryptionKeyResolver = (_, securityToken, keyId, _) => + { + var decryptionKeys = resolveTokenDecryptionKeys(securityToken.Issuer); + + if (keyId.HasValue()) + decryptionKeys = decryptionKeys.WhereAsync(key => key.KeyId == keyId); + + return decryptionKeys.SelectAsync(key => key.ToSecurityKey()).ToListAsync().Result; + }; + + var handler = new JwtSecurityTokenHandler(); + SecurityToken token; + try + { + handler.ValidateToken(jwt, tokenValidationParameters, out token); + } + catch (Exception ex) + { + return new JwtValidationError(JwtError.InvalidToken, ex.Message); + } + + var jwToken = (JwtSecurityToken)token; + + var result = new JsonWebToken + { + Header = + { + Type = jwToken.Header.Typ, + Algorithm = jwToken.Header.Alg, + }, + Payload = + { + JwtId = jwToken.Id, + IssuedAt = jwToken.IssuedAt, + NotBefore = jwToken.ValidFrom, + ExpiresAt = jwToken.ValidTo, + Issuer = jwToken.Issuer, + Audiences = jwToken.Audiences, + } + }; + + foreach (var claim in jwToken.Claims.ExceptBy(JwtSecurityTokenHandlerConstants.ClaimTypesToExclude, claim => claim.Type)) + { + result.Payload[claim.Type] = ToJsonNode(claim.ValueType, claim.Value); + } + + return new ValidJsonWebToken(result); + } + + + /// + /// Creates a representation of a claim value based on its type. + /// + /// The type of the claim value. + /// The string representation of the claim value. + /// A representing the claim value. + private static JsonNode? ToJsonNode(string valueType, string value) + => valueType switch + { + JsonClaimValueTypes.Json => JsonNode.Parse(value).NotNull(nameof(value)), + + ClaimValueTypes.Boolean => JsonValue.Create(bool.Parse(value)), + ClaimValueTypes.Integer => JsonValue.Create(long.Parse(value)), + ClaimValueTypes.Integer32 => JsonValue.Create(int.Parse(value)), + ClaimValueTypes.Integer64 => JsonValue.Create(long.Parse(value)), + + _ => value, + }; +} diff --git a/Abblix.Jwt/JwtClaimTypes.cs b/Abblix.Jwt/JwtClaimTypes.cs new file mode 100644 index 00000000..39fdeed3 --- /dev/null +++ b/Abblix.Jwt/JwtClaimTypes.cs @@ -0,0 +1,163 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Provides constants for JWT claim types. +/// +public static class JwtClaimTypes +{ + /// + /// The 'typ' claim represents the type of the JWT. + /// + public const string Type = "typ"; + + /// + /// The 'alg' (algorithm) claim identifies the cryptographic algorithm used to secure the JWT. + /// It is typically found in the JWT header. + /// + public const string Algorithm = "alg"; + + /// + /// The 'idp' claim represents the identity provider that authenticated the end user. + /// + public const string IdentityProvider = "idp"; + + /// + /// The 'events' claim represents the events associated with the authentication. + /// + public const string Events = "events"; + + /// + /// The 'scope' claim represents the scope of access requested. + /// + public const string Scope = "scope"; + + /// + /// The 'requested_claims' claim represents the specific claims requested by the client. + /// + public const string RequestedClaims = "requested_claims"; + + /// + /// The 'sub' (subject) claim identifies the principal that is the subject of the JWT. + /// Typically used to represent the user or entity the token is about. + /// + public const string Subject = IanaClaimTypes.Sub; + + /// + /// The 'sid' (session ID) claim identifies the session to which the JWT is linked. + /// Useful for maintaining state between the client and the issuer. + /// + public const string SessionId = IanaClaimTypes.Sid; + + /// + /// The 'iss' (issuer) claim identifies the principal that issued the JWT. + /// It is typically a URI identifying the issuer. + /// + public const string Issuer = IanaClaimTypes.Iss; + + /// + /// The 'nonce' claim provides a string value used to associate a client session with an ID token. + /// + public const string Nonce = IanaClaimTypes.Nonce; + + /// + /// The 'aud' (audience) claim identifies the recipients that the JWT is intended for. + /// + public const string Audience = IanaClaimTypes.Aud; + + /// + /// The 'jti' (JWT ID) claim provides a unique identifier for the JWT. + /// + public const string JwtId = IanaClaimTypes.Jti; + + /// + /// The 'auth_time' claim represents the time when the authentication occurred. + /// It is expressed as the number of seconds since Unix epoch. + /// + public const string AuthenticationTime = IanaClaimTypes.AuthTime; + + /// + /// The 'client_id' claim represents the identifier for the client that requested the authentication. + /// Often used in OAuth 2.0 and OpenID Connect flows. + /// + public const string ClientId = IanaClaimTypes.ClientId; + + /// + /// The 'acr' (Authentication Context Class Reference) claim provides the reference values for the authentication context class. + /// + public const string AuthContextClassRef = IanaClaimTypes.Acr; + + /// + /// The 'email' claim represents the user's email address. + /// + public const string Email = IanaClaimTypes.Email; + + /// + /// The 'email_verified' claim is a boolean that is true if the user's email address has been verified; otherwise, it is false. + /// + public const string EmailVerified = IanaClaimTypes.EmailVerified; + + /// + /// The 'phone_number' claim represents the user's phone number. + /// + public const string PhoneNumber = IanaClaimTypes.PhoneNumber; + + /// + /// The 'phone_number_verified' claim is a boolean that is true if the user's phone number has been verified; otherwise, it is false. + /// + public const string PhoneNumberVerified = IanaClaimTypes.PhoneNumberVerified; + + /// + /// The 'c_hash' claim is used for the code hash value in OpenID Connect. + /// It is a hash of the authorization code issued by the authorization server. + /// + public const string CodeHash = IanaClaimTypes.CHash; + + /// + /// The 'at_hash' claim is used for the access token hash value in OpenID Connect. + /// It provides validation that the access token is tied to the identity token. + /// + public const string AccessTokenHash = IanaClaimTypes.AtHash; + + /// + /// The 'iat' (issued at) claim identifies the time at which the JWT was issued. + /// It is expressed as the number of seconds since the Unix epoch. + /// This claim can be used to determine the age of the JWT. + /// + public const string IssuedAt = IanaClaimTypes.Iat; + + /// + /// The 'nbf' (not before) claim identifies the time before which the JWT must not be accepted for processing. + /// It is expressed as the number of seconds since the Unix epoch. + /// This claim is used to define the earliest time at which the JWT is considered valid. + /// + public const string NotBefore = IanaClaimTypes.Nbf; + + /// + /// The 'exp' (expiration time) claim identifies the expiration time on or after which the JWT must not be accepted for processing. + /// It is expressed as the number of seconds since the Unix epoch. + /// This claim is used to define the maximum lifespan of the JWT. + /// + public const string ExpiresAt = IanaClaimTypes.Exp; +} diff --git a/Abblix.Jwt/JwtError.cs b/Abblix.Jwt/JwtError.cs new file mode 100644 index 00000000..24c15989 --- /dev/null +++ b/Abblix.Jwt/JwtError.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Enumerates the different types of JWT-related errors. +/// +public enum JwtError +{ + /// + /// Indicates that the token is invalid. + /// This can be due to various reasons such as incorrect format, signature issues, or payload inconsistencies. + /// + InvalidToken, + + /// + /// Indicates that the token has already been used. + /// This error is typically encountered in scenarios where tokens are meant for single use, such as one-time authorization tokens. + /// + TokenAlreadyUsed, + + /// + /// Indicates that the token has been revoked. + /// A revoked token is no longer valid for use, typically due to security reasons or changes in the access rights of the user. + /// + TokenRevoked, +} diff --git a/Abblix.Jwt/JwtSecurityTokenHandlerConstants.cs b/Abblix.Jwt/JwtSecurityTokenHandlerConstants.cs new file mode 100644 index 00000000..dcc9f79f --- /dev/null +++ b/Abblix.Jwt/JwtSecurityTokenHandlerConstants.cs @@ -0,0 +1,53 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Defines constants used with JwtSecurityTokenHandler, particularly for specifying claim types +/// that should be excluded during certain operations. +/// +internal static class JwtSecurityTokenHandlerConstants +{ + /// + /// An array of claim types that are often excluded from JWT token processing. + /// These claims are typically handled specially by JWT security token handlers + /// due to their significance in the JWT standard and security implications. + /// + public static readonly string[] ClaimTypesToExclude = { + + // Issuer claim, identifies the principal that issued the JWT. + IanaClaimTypes.Iss, + + // Audience claim, identifies the recipients that the JWT is intended for. + IanaClaimTypes.Aud, + + // Expiration time claim, specifies the expiration time on or after which the JWT must not be accepted. + IanaClaimTypes.Exp, + + // Not before claim, specifies the time before which the JWT must not be accepted. + IanaClaimTypes.Nbf, + + // Issued at claim, indicates the time at which the JWT was issued. + IanaClaimTypes.Iat, + }; +} diff --git a/Abblix.Jwt/JwtValidationError.cs b/Abblix.Jwt/JwtValidationError.cs new file mode 100644 index 00000000..36effd85 --- /dev/null +++ b/Abblix.Jwt/JwtValidationError.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Represents an error encountered during the validation of a JSON Web Token (JWT). +/// This record extends the indicating a validation failure. +/// +/// The specific type of JWT error encountered. +/// A description of the error providing details about the validation failure. +public record JwtValidationError(JwtError Error, string ErrorDescription) + : JwtValidationResult; diff --git a/Abblix.Jwt/JwtValidationResult.cs b/Abblix.Jwt/JwtValidationResult.cs new file mode 100644 index 00000000..7aa09e03 --- /dev/null +++ b/Abblix.Jwt/JwtValidationResult.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Serves as the base record for representing the outcome of a JSON Web Token (JWT) validation process. +/// This abstract record is intended to be inherited by more specific result types that indicate either +/// successful validation or various types of validation errors. +/// +public abstract record JwtValidationResult; diff --git a/Abblix.Jwt/PublicKeyUsages.cs b/Abblix.Jwt/PublicKeyUsages.cs new file mode 100644 index 00000000..87a2025a --- /dev/null +++ b/Abblix.Jwt/PublicKeyUsages.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Provides constants for specifying the intended usage of a public key. +/// This class defines different types of public key usages for cryptographic operations, +/// typically used in JSON Web Key (JWK) specifications and related contexts. +/// +public static class PublicKeyUsages +{ + /// + /// Indicates that the public key is intended for use in digital signature operations. + /// + public const string Signature = "sig"; + + /// + /// Indicates that the public key is intended for use in encryption operations. + /// + public const string Encryption = "enc"; +} diff --git a/Abblix.Jwt/ServiceCollectionExtensions.cs b/Abblix.Jwt/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..5c9798f3 --- /dev/null +++ b/Abblix.Jwt/ServiceCollectionExtensions.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.Extensions.DependencyInjection; + +namespace Abblix.Jwt; + +/// +/// Provides extension methods for to register JwT-related services within the application. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers services for creating and validating JSON Web Tokens (JWTs) within the application. + /// + /// + /// This method adds services for JWT handling, enabling the application to generate and validate JWTs efficiently. + /// JWTs are an essential part of modern web application security, used for representing claims securely between + /// two parties. + /// + /// By registering these services, the application can: + /// - Create JWTs with , allowing for the generation of tokens that can securely + /// transmit information between parties. + /// - Validate JWTs with , ensuring that incoming tokens are valid and + /// have not been tampered with. + /// + /// This setup is crucial for implementing authentication and authorization mechanisms that rely on JWTs, + /// such as OAuth 2.0 and OpenID Connect. + /// + /// The to configure with JWT services. + /// The configured , enabling further chaining of service registrations. + public static IServiceCollection AddJsonWebTokens(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton(); + } +} diff --git a/Abblix.Jwt/SigningAlgorithms.cs b/Abblix.Jwt/SigningAlgorithms.cs new file mode 100644 index 00000000..04326e6f --- /dev/null +++ b/Abblix.Jwt/SigningAlgorithms.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Provides constants for various signing algorithms used in JWT and cryptographic operations. +/// +public static class SigningAlgorithms +{ + /// + /// Represents the RS256 (RSA Signature with SHA-256) signing algorithm. + /// This algorithm is commonly used for creating JWT signatures using RSA keys with SHA-256 hashing. + /// + public const string RS256 = "RS256"; + + /// + /// Represents the "none" signing algorithm. + /// This value is used when no digital signature or MAC operation is performed on the JWT. + /// It is important to use this algorithm with caution as it implies that the JWT is unprotected. + /// + public const string None = "none"; +} diff --git a/Abblix.Jwt/ValidJsonWebToken.cs b/Abblix.Jwt/ValidJsonWebToken.cs new file mode 100644 index 00000000..3aa8660f --- /dev/null +++ b/Abblix.Jwt/ValidJsonWebToken.cs @@ -0,0 +1,35 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Represents a successful JWT validation result, encapsulating a validated JsonWebToken. +/// +/// The JsonWebToken instance that has been successfully validated. +public record ValidJsonWebToken(JsonWebToken Token) : JwtValidationResult +{ + /// + /// Gets the successfully validated JsonWebToken. + /// + public JsonWebToken Token { get; } = Token; +} diff --git a/Abblix.Jwt/ValidationOptions.cs b/Abblix.Jwt/ValidationOptions.cs new file mode 100644 index 00000000..403b9193 --- /dev/null +++ b/Abblix.Jwt/ValidationOptions.cs @@ -0,0 +1,68 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Enumeration for specifying various validation options for JWT tokens. +/// These options can be combined using bitwise operations to create a customized set of validation rules. +/// +[Flags] +public enum ValidationOptions +{ + /// + /// Default validation options that include validating the issuer, audience, presence of a signature, + /// validation of the issuer's signing key, and the token's lifetime. + /// This is a common set of validations providing a standard level of security. + /// + Default = ValidateIssuer | ValidateAudience | RequireSignedTokens | ValidateIssuerSigningKey | ValidateLifetime, + + /// + /// Validates the issuer of the JWT. + /// Ensures that the issuer claim (iss) matches a specified value, typically configured in the token validation parameters. + /// + ValidateIssuer = 1 << 0, + + /// + /// Validates the audience of the JWT. + /// Ensures that the audience claim (aud) matches one of the specified values, typically configured in the token validation parameters. + /// + ValidateAudience = 1 << 1, + + /// + /// Requires that the JWT has a valid signature. + /// This ensures that the token has not been tampered with and is from a trusted issuer. + /// + RequireSignedTokens = 1 << 2, + + /// + /// Validates the signing key of the issuer. + /// Ensures that the key used to sign the JWT is valid and is authorized by the issuer. + /// + ValidateIssuerSigningKey = 1 << 3, + + /// + /// Validates the lifetime of the JWT. + /// Ensures that the token is within its valid time frame of use (not expired and not yet valid if the 'nbf' claim is specified). + /// + ValidateLifetime = 1 << 4, +} diff --git a/Abblix.Jwt/ValidationParameters.cs b/Abblix.Jwt/ValidationParameters.cs new file mode 100644 index 00000000..777c864d --- /dev/null +++ b/Abblix.Jwt/ValidationParameters.cs @@ -0,0 +1,84 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Jwt; + +/// +/// Represents the parameters used for validating a JSON Web Token (JWT). +/// +public record ValidationParameters +{ + /// + /// Gets or sets the validation options. + /// + public ValidationOptions Options { get; init; } = ValidationOptions.Default; + + /// + /// Gets or sets the delegate for issuer validation. + /// + public ValidateIssuersDelegate? ValidateIssuer { get; set; } + + /// + /// Gets or sets the delegate for audience validation. + /// + public ValidateAudienceDelegate? ValidateAudience { get; set; } + + /// + /// Gets or sets the delegate for resolving issuer signing keys. + /// + public ResolveIssuerSigningKeysDelegate? ResolveIssuerSigningKeys { get; set; } + + /// + /// Gets or sets the delegate for resolving token decryption keys. + /// + public ResolveTokenDecryptionKeysDelegate? ResolveTokenDecryptionKeys { get; set; } + + /// + /// Represents a delegate that asynchronously resolves a collection of JSON Web Keys (JWKs) for a given issuer, + /// used for validating the signing of a JWT. + /// + /// The issuer for which to resolve the signing keys. + /// An asynchronous enumerable of JSON Web Keys. + public delegate IAsyncEnumerable ResolveIssuerSigningKeysDelegate(string issuer); + + /// + /// Represents a delegate that asynchronously resolves a collection of JSON Web Keys (JWKs) for a given issuer, + /// used for token decryption. + /// + /// The issuer for which to resolve the decryption keys. + /// An asynchronous enumerable of JSON Web Keys. + public delegate IAsyncEnumerable ResolveTokenDecryptionKeysDelegate(string issuer); + + /// + /// Represents a delegate that validates a set of audiences against a specific criterion. + /// + /// The audiences to validate. + /// A task that represents the asynchronous validation operation. The task result contains the validation outcome. + public delegate Task ValidateAudienceDelegate(IEnumerable audiences); + + /// + /// Represents a delegate that validates an issuer against a specific criterion. + /// + /// The issuer to validate. + /// A task that represents the asynchronous validation operation. The task result contains the validation outcome. + public delegate Task ValidateIssuersDelegate(string issuer); +}; diff --git a/Abblix.Oidc.Server.Mvc/Abblix.Oidc.Server.Mvc.csproj b/Abblix.Oidc.Server.Mvc/Abblix.Oidc.Server.Mvc.csproj new file mode 100644 index 00000000..ba8ae871 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Abblix.Oidc.Server.Mvc.csproj @@ -0,0 +1,47 @@ + + + + net6.0;net7.0;net8.0 + enable + enable + Linux + net6.0;net7.0;net8.0 + true + true + Abblix.OIDC.Server.MVC + Abblix OIDC Server Model-View-Controller + The package integrates Abblix's OIDC Server capabilities with ASP.NET MVC, offering seamless support for OpenID Connect in MVC applications. It enables fast and easy implementation of secure OpenID Connect protocols, ensuring security features are accessible within the MVC framework. + Abblix LLP + https://www.abblix.com/abblix-oidc-server + https://github.com/Abblix/Oidc.Server + git + Abblix OIDC OpenID OpenID-Connect Authentication Authorization Security Identity OAuth OAuth2 SSO Single-Sign-On ASP.NET IdentityServer Federation Claims WebApi + README.md + LICENSE.md + Copyright (c) 2024 Abblix LLP. All rights reserved. + For detailed release notes, visit: https://github.com/Abblix/Oidc.Server/releases + Abblix.png + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultDecorator.cs b/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultDecorator.cs new file mode 100644 index 00000000..a6be3afa --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultDecorator.cs @@ -0,0 +1,73 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.ActionResults; + +/// +/// Decorates an by applying additional actions to the HTTP response. +/// +internal class ActionResultDecorator : ActionResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The inner to be executed. + /// The action to apply to the . + public ActionResultDecorator( + ActionResult innerResult, + Action applyToResponseAction) + { + _innerResult = innerResult; + _applyToResponseAction = applyToResponseAction; + } + + private readonly ActionResult _innerResult; + private readonly Action _applyToResponseAction; + + /// + /// Executes the result operation of the action method asynchronously. + /// This method is called by MVC to process the result of an action method. + /// The method applies additional actions to the response and then executes the inner result. + /// + /// The context in which the result is executed. + /// A task that represents the asynchronous execute operation. + public override Task ExecuteResultAsync(ActionContext context) + { + _applyToResponseAction(context.HttpContext.Response); + return _innerResult.ExecuteResultAsync(context); + } + + /// + /// Executes the result operation of the action method synchronously. + /// This method is called by MVC to process the result of an action method. + /// The method applies additional actions to the response and then executes the inner result. + /// + /// The context in which the result is executed. + public override void ExecuteResult(ActionContext context) + { + _applyToResponseAction(context.HttpContext.Response); + _innerResult.ExecuteResult(context); + } +} diff --git a/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultExtensions.cs b/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultExtensions.cs new file mode 100644 index 00000000..db2b5422 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ActionResults/ActionResultExtensions.cs @@ -0,0 +1,50 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc; +using CookieOptions = Microsoft.AspNetCore.Http.CookieOptions; + +namespace Abblix.Oidc.Server.Mvc.ActionResults; + +public static class ActionResultExtensions +{ + /// + /// Decorates an to append a cookie to the response. + /// + /// The to decorate. + /// The name of the cookie to append. + /// The value of the cookie. + /// The to configure the cookie. + /// A decorated that appends the specified cookie. + public static ActionResult WithAppendCookie(this ActionResult innerResult, string name, string value, CookieOptions options) + => new ActionResultDecorator(innerResult, response => response.Cookies.Append(name, value, options)); + + /// + /// Decorates an to delete a cookie from the response. + /// + /// The to decorate. + /// The name of the cookie to delete. + /// The to configure the deletion of the cookie. + /// A decorated that deletes the specified cookie. + public static ActionResult WithDeleteCookie(this ActionResult innerResult, string name, CookieOptions options) + => new ActionResultDecorator(innerResult, response => response.Cookies.Delete(name, options)); +} diff --git a/Abblix.Oidc.Server.Mvc/ActionResults/CookieOptionsExtensions.cs b/Abblix.Oidc.Server.Mvc/ActionResults/CookieOptionsExtensions.cs new file mode 100644 index 00000000..6e07b6f4 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ActionResults/CookieOptionsExtensions.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; +using Microsoft.AspNetCore.Http; + +namespace Abblix.Oidc.Server.Mvc.ActionResults; +public static class CookieOptionsExtensions +{ + /// + /// Converts custom to ASP.NET Core's . + /// + /// The custom cookie options to convert. + /// The converted suitable for ASP.NET Core. + public static CookieOptions ConvertOptions(this Common.CookieOptions options) => new() + { + Domain = options.Domain, + Path = options.Path, + Secure = options.Secure, + IsEssential = options.IsEssential, + HttpOnly = options.HttpOnly, + SameSite = options.SameSite.ConvertSameSite(), + Expires = options.Expires, + MaxAge = options.MaxAge, + }; + + /// + /// Converts a string representation of the SameSite attribute to its equivalent. + /// + /// The string representation of the SameSite attribute. + /// The value corresponding to the input string. + private static SameSiteMode ConvertSameSite(this string? sameSite) + { + return sameSite.HasValue() + ? Enum.Parse(sameSite, true) + : SameSiteMode.Unspecified; + } +} diff --git a/Abblix.Oidc.Server.Mvc/ActionResults/FrontChannelLogoutResult.cs b/Abblix.Oidc.Server.Mvc/ActionResults/FrontChannelLogoutResult.cs new file mode 100644 index 00000000..21cf06a2 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ActionResults/FrontChannelLogoutResult.cs @@ -0,0 +1,96 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Web; +using System.Xml; + +namespace Abblix.Oidc.Server.Mvc.ActionResults; + +/// +/// Generates an HTML response that handles front-channel logout by embedding iframes for each logout URI. +/// +/// +/// This class is responsible for creating an HTML page containing iframes for each URI in the front-channel logout process. +/// The iframes are used to send logout requests to all clients participating in the user's session. +/// The 'barrierSync' function ensures that the user is redirected to the post-logout URI only after all iframes have loaded, +/// indicating that logout requests have been sent to all clients. +/// +public class FrontChannelLogoutResult : GeneratedHtmlResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The URI to redirect to after the front-channel logout process is complete. + /// A list of URIs for the iframes to be used in the front-channel logout process. + public FrontChannelLogoutResult( + Uri? postLogOutUri, + IList frontChannelLogoutUris) + { + _postLogOutUri = postLogOutUri; + _frontChannelLogoutUris = frontChannelLogoutUris; + } + + private readonly IList _frontChannelLogoutUris; + private readonly Uri? _postLogOutUri; + + /// + /// Asynchronously writes the HTML content that includes iframes for each front-channel logout URI and + /// a script to redirect to the post-logout URI. + /// + /// The XML writer used to write the HTML content. + protected override async Task WriteHtmlAsync(XmlWriter writer) + { + await writer.WriteDocTypeAsync("html", null, null, null); + + writer.WriteStartElement("html"); + writer.WriteStartElement("head"); + + writer.WriteElementString("style", "iframe { display: none; width: 0; height: 0; }"); + writer.WriteStartElement("script"); + await writer.WriteStringAsync($"var count = {_frontChannelLogoutUris.Count}; "); + + if (_postLogOutUri != null) + { + var postLogOutUri = HttpUtility.JavaScriptStringEncode(_postLogOutUri.OriginalString, true); + + await writer.WriteStringAsync("function barrierSync() { "); + await writer.WriteStringAsync("if (--count === 0) "); + await writer.WriteStringAsync($"window.location.replace({postLogOutUri}); "); + await writer.WriteStringAsync("}"); + } + + await writer.WriteEndElementAsync(); // + await writer.WriteEndElementAsync(); // + + writer.WriteStartElement("body"); + foreach (var uri in _frontChannelLogoutUris) + { + writer.WriteStartElement("iframe"); + writer.WriteAttributeString("onload", "barrierSync()"); + writer.WriteAttributeString("src", uri.OriginalString); + await writer.WriteEndElementAsync(); // + } + + await writer.WriteEndElementAsync(); // + await writer.WriteEndElementAsync(); // + } +} diff --git a/Abblix.Oidc.Server.Mvc/ActionResults/GeneratedHtmlResult.cs b/Abblix.Oidc.Server.Mvc/ActionResults/GeneratedHtmlResult.cs new file mode 100644 index 00000000..61404b5e --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ActionResults/GeneratedHtmlResult.cs @@ -0,0 +1,92 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using System.Xml; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Net.Http.Headers; + +namespace Abblix.Oidc.Server.Mvc.ActionResults; + +/// +/// Provides a base class for action results that generate HTML content. +/// +/// +/// This abstract class is designed to be inherited by classes that need to dynamically generate HTML content as an action result. +/// It sets various HTTP headers to ensure that the generated content is not cached and is served as HTML. +/// +public abstract class GeneratedHtmlResult : ActionResult, IStatusCodeActionResult +{ + /// + /// Gets the HTTP status code to be set in the response. + /// Defaults to 200 (OK) if not set. + /// + public int? StatusCode { init; get; } = StatusCodes.Status200OK; + + /// + /// Asynchronously executes the result operation of the action method, setting HTTP headers and writing the HTML content to the response. + /// + /// The action context for the current request. + public override async Task ExecuteResultAsync(ActionContext context) + { + var response = context.HttpContext.Response; + + if (StatusCode != null) + response.StatusCode = StatusCode.Value; + + var headers = response.GetTypedHeaders(); + headers.Expires = DateTimeOffset.UnixEpoch; + headers.CacheControl = new CacheControlHeaderValue + { + MaxAge = TimeSpan.Zero, + SharedMaxAge = TimeSpan.Zero, + NoStore = true, + NoCache = true, + }; + response.Headers.Pragma = "no-cache"; + response.ContentType = MediaTypeNames.Text.Html; + + await using var streamWriter = new StreamWriter( + response.Body, + leaveOpen: true); + + await using var xmlWriter = XmlWriter.Create( + streamWriter, + new XmlWriterSettings + { + OmitXmlDeclaration = true, + Indent = false, + CloseOutput = false, + Async = true, + }); + + await WriteHtmlAsync(xmlWriter); + } + + /// + /// When overridden in a derived class, writes the HTML content to be rendered to the specified XML writer. + /// + /// The XML writer used to write the HTML content. + protected abstract Task WriteHtmlAsync(XmlWriter writer); +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/AbsoluteUriAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/AbsoluteUriAttribute.cs new file mode 100644 index 00000000..c5301585 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/AbsoluteUriAttribute.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// A validation attribute that ensures a property, field, or parameter is an absolute URI. +/// Optionally, it can also require a specific URI scheme. +/// +/// +/// This attribute can be applied to properties, fields, or parameters that are expected to represent absolute URIs. +/// If a specific scheme is required, it can be set using the property. +/// The absence of a value is considered valid; use the +/// in combination with this attribute to enforce non-null/empty values. +/// +public sealed class AbsoluteUriAttribute : ValidationAttribute +{ + /// + /// Gets or sets the URI scheme that the URI must use, such as "http" or "https". + /// If not set, any absolute URI scheme is considered valid. + /// + public string? RequireScheme { get; set; } + + /// + /// Determines whether the specified value of the object is valid. + /// + /// The value of the object to validate. + /// The context information about the object being validated. + /// if the value is a valid absolute URI, otherwise an error . + protected override ValidationResult? IsValid(object? value, ValidationContext context) + => value switch + { + null => ValidationResult.Success, // absence is OK, apply [Required] if needed + + string str when string.IsNullOrEmpty(str) => ValidationResult.Success, + Uri uri when string.IsNullOrEmpty(uri.OriginalString) => ValidationResult.Success, + + string str when Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out var uri) => IsValid(uri, context), + + Uri { IsAbsoluteUri: true, Scheme: var scheme } when RequireScheme.HasValue() && !string.Equals(RequireScheme, scheme, StringComparison.OrdinalIgnoreCase) + => new ValidationResult($"{context.GetName()} value must use {RequireScheme} scheme."), + + Uri { IsAbsoluteUri: true } => ValidationResult.Success, + + Uri => new ValidationResult($"{context.GetName()} value is not absolute."), + _ => new ValidationResult($"{context.GetName()} is not Uri, but {value.GetType().Name}."), + }; +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/AllowedValuesAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/AllowedValuesAttribute.cs new file mode 100644 index 00000000..67e3c3d7 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/AllowedValuesAttribute.cs @@ -0,0 +1,89 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// A validation attribute to specify a set of allowed values for a property, field, or parameter. +/// Can be applied to single values or collections of strings. +/// +/// +/// This attribute ensures that the value or each value in a collection matches one of the predefined allowed values. +/// It is case-insensitive and supports validation on properties, fields, or parameters that are either +/// single strings or collections of strings (e.g., arrays or lists). +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] +public class AllowedValuesAttribute : ValidationAttribute +{ + /// + /// Initializes a new instance of the class with a set of allowed values. + /// + /// The allowed values that the associated property, field, or parameter can have. + public AllowedValuesAttribute(params string[] allowedValues) + { + _allowedValues = allowedValues; + } + + private readonly string[] _allowedValues; + + /// + /// Determines whether the specified value of the object is valid. + /// + /// The value of the object to validate. + /// The context information about the object being validated. + /// if the value is among the allowed values, + /// otherwise an error . + /// + protected override ValidationResult? IsValid(object? value, ValidationContext context) + { + return value switch + { + null => ValidationResult.Success, + string[][] stringValues => IsValid(stringValues.SelectMany(stringValue => stringValue), context), + string[] stringValues => IsValid(stringValues, context), + string stringValue => IsValid(stringValue), + _ => throw new InvalidOperationException($"The type {value.GetType()} is not supported by {nameof(AllowedValuesAttribute)}"), + }; + } + + private ValidationResult? IsValid(IEnumerable values, ValidationContext context) + { + foreach (var value in values) + { + var result = IsValid(value); + if (result != ValidationResult.Success) + return result; + } + + return ValidationResult.Success; + } + + private ValidationResult? IsValid(string value) + { + if (!_allowedValues.Contains(value, StringComparer.OrdinalIgnoreCase)) + return new ValidationResult($"The value '{value}' is invalid"); + + return ValidationResult.Success; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/ConditionalRequiredAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/ConditionalRequiredAttribute.cs new file mode 100644 index 00000000..2f4ef2e1 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/ConditionalRequiredAttribute.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// An abstract attribute that extends the standard to include conditional logic. +/// This attribute allows properties to be required based on the state of the associated object. +/// +/// +/// Implement this abstract class to define custom conditions under which the attributed property is required. +/// The requirement logic is determined by the method, which must be implemented in derived classes. +/// +public abstract class ConditionalRequiredAttribute : RequiredAttribute +{ + /// + /// Determines whether the specified value of the object is valid based on a custom condition. + /// + /// The value of the object to validate. + /// The context information about the object being validated. + /// + /// if the condition is not met or if the value is valid as per the base ; + /// otherwise, an error . + /// + protected override ValidationResult? IsValid(object? value, ValidationContext context) + { + if (!IsRequired(context.ObjectInstance)) + return ValidationResult.Success; + + return base.IsValid(value, context); + } + + /// + /// When implemented in a derived class, determines whether the attribute's requirement condition is met. + /// + /// The object instance associated with the attribute. + /// true if the attribute's requirement condition is met; otherwise, false. + protected abstract bool IsRequired(object model); +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/ElementsRequiredAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/ElementsRequiredAttribute.cs new file mode 100644 index 00000000..e9f06aa9 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/ElementsRequiredAttribute.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Collections; +using System.ComponentModel.DataAnnotations; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// An attribute that ensures all elements within an enumerable collection are non-null. +/// +/// +/// Use this attribute to validate that collections do not contain any null elements. +/// It extends the standard and overrides the method. +/// +public sealed class ElementsRequiredAttribute : ValidationAttribute +{ + /// + /// Determines whether the specified value of the object is valid. + /// + /// The value of the object to validate. + /// + /// true if the value is either not an enumerable collection, or if it's a collection with all non-null elements; + /// otherwise, false. + /// + public override bool IsValid(object? value) + => value switch + { + IEnumerable collection => collection.Cast().All(item => item != null), + _ => true, + }; + + /// + /// Formats the error message to display if the validation fails. + /// + /// The name to include in the formatted message. + /// A string containing the formatted error message. + public override string FormatErrorMessage(string name) + => $"Each element of the {name} must be non-null."; +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/FromQueryOrFormAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/FromQueryOrFormAttribute.cs new file mode 100644 index 00000000..6f5443b9 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/FromQueryOrFormAttribute.cs @@ -0,0 +1,62 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// An attribute that specifies binding from either the query string or the form data. +/// +/// +/// This attribute is useful in scenarios where a value can be provided either through the query string or form data. +/// It implements and extends . +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter)] +public class FromQueryOrFormAttribute : BindAttribute, IBindingSourceMetadata +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// By default, the prefix is set to an empty string, indicating no specific prefix for the bound properties. + /// + public FromQueryOrFormAttribute() + { + Prefix = ""; + } + + /// + /// Gets the binding source for this attribute, combining both Query and Form sources. + /// + /// + /// Specifies that the binding source is a composite of both Query and Form sources. + /// + public BindingSource BindingSource => CompositeBindingSource.Create( + new[] + { + BindingSource.Query, + BindingSource.Form, + }, + nameof(FromQueryOrFormAttribute)); +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/HttpGetOrPostAttribute.cs b/Abblix.Oidc.Server.Mvc/Attributes/HttpGetOrPostAttribute.cs new file mode 100644 index 00000000..feee9124 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/HttpGetOrPostAttribute.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc.Routing; +using static Microsoft.AspNetCore.Http.HttpMethods; + + + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// Specifies that an action supports both HTTP GET and POST methods. +/// +/// +/// This attribute can be applied to an action method to indicate that it should +/// respond to HTTP GET and POST requests. When applied to an action method, it specifies +/// that the method handles requests made with these two HTTP methods. It can be used to +/// support scenarios where a resource can be fetched (GET) or submitted (POST) to the same URL. +/// +public class HttpGetOrPostAttribute : HttpMethodAttribute +{ + private static readonly IEnumerable SupportedMethods = new[] { Get, Post }; + + /// + /// Initializes a new instance of the class without specifying a route template. + /// + public HttpGetOrPostAttribute() + : base(SupportedMethods) + { + } + + /// + /// Initializes a new instance of the class with the specified route template. + /// + /// The route template. The template may define path segments, parameters, etc., as per routing conventions. + public HttpGetOrPostAttribute(string template) + : base(SupportedMethods, template) + { + } +} diff --git a/Abblix.Oidc.Server.Mvc/Attributes/ValidationContextExtensions.cs b/Abblix.Oidc.Server.Mvc/Attributes/ValidationContextExtensions.cs new file mode 100644 index 00000000..31c38596 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Attributes/ValidationContextExtensions.cs @@ -0,0 +1,64 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Attributes; + +/// +/// Provides extension methods for . +/// +internal static class ValidationContextExtensions +{ + /// + /// Retrieves the name of the member associated with the validation context, + /// taking into account custom attributes that might alter the displayed name. + /// + /// The instance. + /// + /// The name of the member for display purposes. This could be the value set in + /// or , if they are applied. + /// Otherwise, it defaults to the display name provided by the context. + /// + /// + /// This method first checks if the member has a or + /// applied, and if so, returns the specified name. + /// If these attributes are not present, it falls back to the default display name. + /// + public static string? GetName(this ValidationContext context) + { + if (context.MemberName == null) + return null; + + var member = context.ObjectType.GetMember(context.MemberName).SingleOrDefault(); + + return member switch + { + not null when member.GetCustomAttribute() is { Name: var name } => name, + not null when member.GetCustomAttribute() is { Name: var name } => name, + _ => context.DisplayName, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/AuthenticationSchemeAdapter.cs b/Abblix.Oidc.Server.Mvc/AuthenticationSchemeAdapter.cs new file mode 100644 index 00000000..f8fe3508 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/AuthenticationSchemeAdapter.cs @@ -0,0 +1,158 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Claims; +using System.Text.Json; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Utils; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Adapts ASP.NET Authentication Scheme to the interface. +/// This adapter allows the integration of the Abblix OIDC Server with standard ASP.NET authentication mechanisms, +/// enabling the use of existing authentication schemes to manage OIDC sessions. +/// +public class AuthenticationSchemeAdapter : IAuthSessionService +{ + /// + /// Initializes a new instance of the class, + /// injecting dependencies needed to access and manage HTTP contexts. + /// + /// Provides access to the , + /// allowing operations on the HTTP context of the current request. + public AuthenticationSchemeAdapter(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + private readonly IHttpContextAccessor _httpContextAccessor; + + /// + /// Provides direct access to the current by ensuring it is available and not null. + /// + private HttpContext HttpContext => _httpContextAccessor.HttpContext.NotNull(nameof(IHttpContextAccessor.HttpContext)); + + /// + /// Asynchronously retrieves the current user's authentication session if available. + /// This method wraps ASP.NET's built-in authentication mechanisms to provide an model. + /// + /// + /// An asynchronous stream of instances representing the user's current + /// authentication sessions. + /// + public async IAsyncEnumerable GetAvailableAuthSessions() + { + var user = await AuthenticateAsync(); + if (user != null) + { + yield return user; + } + } + + /// + /// Attempts to authenticate the current user based on the configured default authentication scheme, + /// converting the authentication results into an . + /// + /// + /// A task that represents the asynchronous operation. The task result contains the + /// of the authenticated user or null if the authentication fails. + /// + public async Task AuthenticateAsync() + { + var authenticationResult = await HttpContext.AuthenticateAsync(); + if (!authenticationResult.Succeeded) + return null; + + var principal = authenticationResult.Principal; + var properties = authenticationResult.Properties; + + var sessionId = properties.GetString(JwtClaimTypes.SessionId); + if (string.IsNullOrEmpty(sessionId)) + { + throw new InvalidOperationException( + $"Use {nameof(SessionIdGenerator)}.{nameof(SessionIdGenerator.GenerateSessionId)}() to generate a new session Id when calling .SignInAsync() method"); + } + + var authenticationTime = properties.GetString(JwtClaimTypes.AuthenticationTime); + if (string.IsNullOrEmpty(authenticationTime)) + { + throw new InvalidOperationException($"There is no {JwtClaimTypes.AuthenticationTime} in the properties"); + } + + if (!principal.IsAuthenticated()) + return null; + + // TODO think about the support for a list of several user accounts below + var authSession = new AuthSession( + principal.FindFirstValue(JwtClaimTypes.Subject).NotNull(JwtClaimTypes.Subject), + sessionId, + DateTimeOffset.FromUnixTimeSeconds(long.Parse(authenticationTime)), + principal.Identity!.AuthenticationType.NotNull(nameof(ClaimsIdentity.AuthenticationType))) + { + AuthContextClassRef = properties.GetString(JwtClaimTypes.AuthContextClassRef), + }; + + var affectedClientIdsJson = properties.GetString(nameof(AuthSession.AffectedClientIds)); + if (affectedClientIdsJson != null) + { + var affectedClientIds = JsonSerializer.Deserialize>(affectedClientIdsJson); + if (affectedClientIds != null) + { + authSession = authSession with { AffectedClientIds = affectedClientIds }; + } + } + + return authSession; + } + + /// + /// Signs in the specified user into the application, setting up their authentication session. + /// + /// The authentication session details to be used for signing in. + /// A task that represents the asynchronous sign-in operation. + public Task SignInAsync(AuthSession authSession) + { + var claims = new[] { new Claim(JwtClaimTypes.Subject, authSession.Subject) }; + var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, authSession.IdentityProvider)); + + var properties = new AuthenticationProperties(); + properties.SetString(JwtClaimTypes.SessionId, authSession.SessionId); + properties.SetString(JwtClaimTypes.AuthenticationTime, authSession.AuthenticationTime.ToUnixTimeSeconds().ToString()); + properties.SetString(JwtClaimTypes.AuthContextClassRef, authSession.AuthContextClassRef); + properties.SetString(nameof(AuthSession.AffectedClientIds), JsonSerializer.Serialize(authSession.AffectedClientIds)); + + return HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, properties); + } + + /// + /// Signs out the current user from the application, ending their authenticated session. + /// + /// A task that represents the asynchronous sign-out operation. + public Task SignOutAsync() => HttpContext.SignOutAsync(); +} diff --git a/Abblix.Oidc.Server.Mvc/AutoPostFormatter.cs b/Abblix.Oidc.Server.Mvc/AutoPostFormatter.cs new file mode 100644 index 00000000..62ecdac2 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/AutoPostFormatter.cs @@ -0,0 +1,130 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using System.Text; +using System.Xml; +using Abblix.Oidc.Server.Mvc.Binders; +using Microsoft.AspNetCore.Mvc.Formatters; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// A custom text output formatter that generates an HTML form with auto-submit functionality. +/// This formatter is designed to output HTML content that automatically submits a POST request to a specified URI +/// with given parameters when loaded in a browser. +/// +public class AutoPostFormatter : TextOutputFormatter +{ + /// + /// Initializes a new instance of the class. + /// + /// Provider to retrieve the parameters for the form. + /// The URI where the form will be submitted. + public AutoPostFormatter(IParametersProvider parametersProvider, Uri action) + { + _parametersProvider = parametersProvider; + _action = action; + + SupportedMediaTypes.Add(MediaTypeNames.Text.Html); + SupportedEncodings.Add(Encoding.UTF8); + } + + private readonly IParametersProvider _parametersProvider; + private readonly Uri _action; + + /// + /// Writes the HTML content to the response body asynchronously. + /// This method overrides the base class implementation to write an HTML form with the specified parameters. + /// + /// The context for the output formatter. + /// The encoding to use for the response. + /// A task that represents the asynchronous write operation. + public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding encoding) + { + if (context.Object == null) + return; + + var settings = new XmlWriterSettings { Async = true, Encoding = encoding }; + await using var writer = XmlWriter.Create(context.HttpContext.Response.Body, settings); + + var parameters = _parametersProvider.GetParameters(context.Object); + + await WriteHtmlAsync(writer, parameters); + } + + /// + /// Asynchronously writes the HTML form elements using the provided . + /// The form is auto-submitted using JavaScript when loaded in the browser. + /// + /// The XML writer to write the HTML content. + /// The collection of parameters to include in the form. + /// A task that represents the asynchronous write operation. + private async Task WriteHtmlAsync( + XmlWriter writer, + IEnumerable<(string name, string? value)> parameters) + { + await writer.WriteDocTypeAsync("html", null, null, null); + writer.WriteStartElement("html"); + { + writer.WriteStartElement("head"); + { + writer.WriteElementString("title", "Working..."); + } + await writer.WriteEndElementAsync(); + + writer.WriteStartElement("body"); + writer.WriteAttributeString("onload", "javascript:document.forms[0].submit()"); + { + writer.WriteStartElement("form"); + writer.WriteAttributeString("method", "POST"); + writer.WriteAttributeString("action", _action.OriginalString); + { + foreach (var (name, value) in parameters) + { + if (string.IsNullOrEmpty(value)) + continue; + + writer.WriteStartElement("input"); + writer.WriteAttributeString("type", "hidden"); + writer.WriteAttributeString("name", name); + writer.WriteAttributeString("value", value); + await writer.WriteEndElementAsync(); + } + + writer.WriteStartElement("noscript"); + { + writer.WriteElementString("p", "JavaScript is disabled. Click Submit to continue."); + writer.WriteStartElement("input"); + writer.WriteAttributeString("type", "submit"); + writer.WriteAttributeString("value", "Submit"); + await writer.WriteEndElementAsync(); //input + } + await writer.WriteEndElementAsync(); //noscript + } + await writer.WriteEndElementAsync(); //form + } + await writer.WriteEndElementAsync(); //body + } + await writer.WriteEndElementAsync(); //html + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/AuthenticationHeaderBinder.cs b/Abblix.Oidc.Server.Mvc/Binders/AuthenticationHeaderBinder.cs new file mode 100644 index 00000000..372e0ddd --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/AuthenticationHeaderBinder.cs @@ -0,0 +1,109 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Headers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// A model binder for binding authentication header values. +/// +/// +/// This binder is specifically designed to extract and bind authentication header values from HTTP requests. +/// It extends the functionality of to handle authentication headers. +/// +public class AuthenticationHeaderBinder : ModelBinderBase +{ + /// + /// Asynchronously binds an authentication header model. + /// + /// The for the binding operation. + /// + /// The method extracts authentication header data from the request and tries to bind it to the model. + /// + public override async Task BindModelAsync(ModelBindingContext bindingContext) + { + var headerValueProvider = new HeaderValueProvider(bindingContext.HttpContext.Request.Headers); + + var bindingInfo = BindingInfo.GetBindingInfo( + bindingContext.ModelType.GetCustomAttributes(false), + bindingContext.ModelMetadata); + + var childBindingContext = DefaultModelBindingContext.CreateBindingContext( + bindingContext.ActionContext, + headerValueProvider, + bindingContext.ModelMetadata, + bindingInfo, + bindingContext.ModelName); + + await base.BindModelAsync(childBindingContext); + + bindingContext.Result = childBindingContext.Result; + } + + /// + /// Tries to parse the authentication header from the provided string values. + /// + /// The type of the model to bind to. + /// The header values to parse. + /// The resulting parsed object if successful. + /// True if parsing is successful, otherwise false. + protected override bool TryParse(Type type, StringValues values, out object? result) + { + if (!AuthenticationHeaderValue.TryParse(values, out var headerValue)) + { + result = null; + return false; + } + + result = headerValue; + return true; + } + + /// + /// Provides values from HTTP headers. + /// + private class HeaderValueProvider : IValueProvider + { + private readonly IHeaderDictionary _headers; + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP header dictionary to provide values from. + public HeaderValueProvider(IHeaderDictionary headers) + { + _headers = headers; + } + + /// + public bool ContainsPrefix(string prefix) + => _headers.ContainsKey(prefix); + + /// + public ValueProviderResult GetValue(string key) + => _headers.TryGetValue(key, out var values) ? new ValueProviderResult(values) : ValueProviderResult.None; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/CultureInfoBinder.cs b/Abblix.Oidc.Server.Mvc/Binders/CultureInfoBinder.cs new file mode 100644 index 00000000..b867ef5b --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/CultureInfoBinder.cs @@ -0,0 +1,101 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// A model binder for binding culture information from model data. +/// +/// +/// This binder is capable of handling culture-specific data by converting string values into objects. +/// It supports binding single objects, arrays, and lists of . +/// +public class CultureInfoBinder : ModelBinderBase, IModelBinderProvider +{ + /// + /// Gets the model binder based on the provided context. + /// + /// The context for the model binding. + /// The model binder for , or null if the model type is not supported. + public IModelBinder? GetBinder(ModelBinderProviderContext context) + { + var type = context.Metadata.ModelType; + + return type == typeof(CultureInfo) || + type.IsAssignableFrom(typeof(CultureInfo[])) || + type.IsAssignableFrom(typeof(List)) + ? this + : null; + } + + /// + /// Tries to parse the provided values into a object or a collection of . + /// + /// The target type for the binding. + /// The values to parse. + /// The parsed result object. + /// True if parsing is successful, otherwise false. + protected override bool TryParse(Type type, StringValues values, out object? result) + { + string? stringValue = values; + if (stringValue == null) + { + result = null; + return false; + } + + if (type == typeof(CultureInfo)) + { + result = new CultureInfo(stringValue); + return true; + } + + if (type.IsAssignableFrom(typeof(CultureInfo[]))) + { + result = GetCultureInfos(values).ToArray(); + return true; + } + + if (type.IsAssignableFrom(typeof(List))) + { + result = GetCultureInfos(values).ToList(); + return true; + } + + result = null; + return false; + } + + /// + /// Extracts an enumerable of objects from the given string values. + /// + /// The string values to parse. + /// An enumerable of . + private static IEnumerable GetCultureInfos(StringValues values) => + from value in values + from culture in value.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + select new CultureInfo(culture); +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/IParametersProvider.cs b/Abblix.Oidc.Server.Mvc/Binders/IParametersProvider.cs new file mode 100644 index 00000000..cb51071e --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/IParametersProvider.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// Defines an interface for providing parameters extracted from an object, akin to a reverse process of binding properties. +/// +public interface IParametersProvider +{ + /// + /// Retrieves parameters as name-value pairs from the specified object, effectively reversing the property binding process. + /// + /// The object from which to extract parameters. + /// A collection of name-value pairs representing the parameters extracted from the object. + /// + /// This method introspects an object and extracts key-value pairs, where the key is the parameter name and + /// the value is the parameter value. + /// This is useful for scenarios such as generating HTTP query parameters, logging, or other situations + /// where complex objects need to be represented as simple key-value pairs. + /// + IEnumerable<(string name, string? value)> GetParameters(object obj); +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/JsonSerializerModelBinder.cs b/Abblix.Oidc.Server.Mvc/Binders/JsonSerializerModelBinder.cs new file mode 100644 index 00000000..8ee2dbc7 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/JsonSerializerModelBinder.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// A model binder that uses JSON serialization to bind data to objects. +/// +/// +/// This model binder utilizes the System.Text.Json.JsonSerializer to deserialize +/// the incoming data into the specified type. It is particularly useful for scenarios +/// where the incoming request data is in JSON format and needs to be converted into +/// complex objects. This binder can be applied to various types of data sources such as +/// query strings, form data, or headers, allowing for flexible data binding from JSON content. +/// +public class JsonSerializerModelBinder : ModelBinderBase +{ + /// + /// Attempts to parse the incoming data and convert it to the specified type using JSON deserialization. + /// + /// The type of object to which the data should be bound. + /// The data to be bound, represented as a collection of string values. + /// The resulting object after deserialization, if successful. + /// Returns true if deserialization is successful; otherwise, false. + protected override bool TryParse(Type type, StringValues values, out object? result) + { + string? stringValue = values; + if (stringValue == null) + { + result = null; + return false; + } + + result = JsonSerializer.Deserialize(stringValue, type); + return true; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/ModelBinderBase.cs b/Abblix.Oidc.Server.Mvc/Binders/ModelBinderBase.cs new file mode 100644 index 00000000..171cdf3d --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/ModelBinderBase.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// Provides a base implementation for a model binder. +/// +/// +/// This abstract class serves as a foundation for custom model binders. +/// It handles common binding tasks and delegates the specific parsing logic +/// to the derived classes through the abstract method. +/// +public abstract class ModelBinderBase : IModelBinder +{ + /// + /// Asynchronously binds the model for a given action method parameter. + /// + /// The context for the model binding process, containing information about + /// the model object, the state of the model binding, and other metadata. + /// A task representing the model binding process. + /// Thrown when the bindingContext is null. + public virtual Task BindModelAsync(ModelBindingContext bindingContext) + { + ArgumentNullException.ThrowIfNull(bindingContext, nameof(bindingContext)); + + var modelValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); + if (modelValue == ValueProviderResult.None) + { + return Task.CompletedTask; + } + + bindingContext.ModelState.SetModelValue(bindingContext.ModelName, modelValue); + + try + { + bindingContext.Result = TryParse(bindingContext.ModelType, modelValue.Values, out var result) + ? ModelBindingResult.Success(result) + : ModelBindingResult.Failed(); + } + catch (Exception ex) + { + bindingContext.ModelState.TryAddModelError( + bindingContext.ModelName, + ex, + bindingContext.ModelMetadata); + } + + return Task.CompletedTask; + } + + /// + /// When implemented in a derived class, attempts to parse the incoming data into the specified type. + /// + /// The type to which the data should be bound. + /// The data to be bound, represented as a collection of string values. + /// The result of the parsing, if successful. + /// True if the parsing is successful; otherwise, false. + protected abstract bool TryParse(Type type, StringValues values, out object? result); +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/ParametersProvider.cs b/Abblix.Oidc.Server.Mvc/Binders/ParametersProvider.cs new file mode 100644 index 00000000..b912a6c8 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/ParametersProvider.cs @@ -0,0 +1,51 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// Provides functionality to extract parameters and their values from an object. +/// +/// +/// This class serializes an object into a JSON element and then enumerates its properties +/// to extract the parameters and their respective values. It implements the interface. +/// +public class ParametersProvider : IParametersProvider +{ + /// + /// Retrieves the parameters and their values from the specified object. + /// + /// The object from which to extract parameters. + /// A collection of tuples, each containing a parameter name and its corresponding value. + /// + /// This method serializes the object to JSON and then iterates through the resulting JSON properties, + /// extracting the names and values as parameters. + /// + public IEnumerable<(string name, string? value)> GetParameters(object obj) + { + return JsonSerializer.SerializeToElement(obj).EnumerateObject() + .Select(property => (property.Name, property.Value.GetString())) + .ToArray(); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/RequiredBindingMetadataProvider.cs b/Abblix.Oidc.Server.Mvc/Binders/RequiredBindingMetadataProvider.cs new file mode 100644 index 00000000..76bf99ca --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/RequiredBindingMetadataProvider.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// Provides binding metadata based on the presence of the on model properties. +/// +/// +/// This class implements the interface and checks if a model property +/// is annotated with the . If so, it sets the binding metadata to indicate that +/// binding is required for that property. +/// +public class RequiredBindingMetadataProvider : IBindingMetadataProvider +{ + /// + /// Creates binding metadata for a given context. If a model property is marked with , + /// this method sets the binding metadata to require binding for that property. + /// + /// The context for the binding metadata provider. + /// + /// The method checks for the presence of in the property attributes of the context. + /// If found, it sets to true, enforcing the requirement for binding. + /// + public void CreateBindingMetadata(BindingMetadataProviderContext context) + { + if (context is { PropertyAttributes: { } attributes } && + attributes.OfType().Any()) + { + context.BindingMetadata.IsBindingRequired = true; + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/SecondsToTimeSpanModelBinder.cs b/Abblix.Oidc.Server.Mvc/Binders/SecondsToTimeSpanModelBinder.cs new file mode 100644 index 00000000..036d4c89 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/SecondsToTimeSpanModelBinder.cs @@ -0,0 +1,61 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// A model binder that converts seconds (as string) to a object. +/// +/// +/// This model binder is useful for binding API parameters that are provided as seconds in string format, +/// and need to be converted to a for internal processing. +/// +public class SecondsToTimeSpanModelBinder : ModelBinderBase +{ + /// + /// Attempts to parse the provided string value representing seconds into a object. + /// + /// The type of the model being bound. Expected to be or compatible. + /// The string values from the request, representing seconds. + /// The parsed object, if successful. + /// + /// true if the parsing succeeds and a valid is created; otherwise, false. + /// + /// + /// This method parses the input string as a long, representing seconds, and converts it to a . + /// If the input string is not a valid long or represents an invalid time duration, the parsing fails. + /// + protected override bool TryParse(Type type, StringValues values, out object? result) + { + string? stringValue = values; + if (stringValue == null) + { + result = null; + return false; + } + + result = TimeSpan.FromSeconds(long.Parse(stringValue)); + return true; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Binders/SpaceSeparatedValuesBinder.cs b/Abblix.Oidc.Server.Mvc/Binders/SpaceSeparatedValuesBinder.cs new file mode 100644 index 00000000..13f98d8c --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Binders/SpaceSeparatedValuesBinder.cs @@ -0,0 +1,58 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.Extensions.Primitives; + +namespace Abblix.Oidc.Server.Mvc.Binders; + +/// +/// A model binder that converts a space-separated string into an array of strings. +/// +/// +/// This binder is useful for processing query parameters or other data represented as a single string +/// with values separated by spaces. It splits the input string by spaces and converts the result into an array. +/// +public class SpaceSeparatedValuesBinder : ModelBinderBase +{ + /// + /// Parses a space-separated string and converts it into an array of strings. + /// + /// The type of the model being bound, expected to be an array of strings. + /// The string values from the request, expected to be space-separated. + /// The parsed array of strings, if successful. + /// + /// Always returns true as the method is designed to handle empty or null inputs gracefully. + /// + /// + /// The method splits the input string by spaces. Each separated segment is treated as an individual string in the resulting array. + /// Empty entries are ignored, so strings with consecutive spaces won't result in empty strings in the array. + /// + protected override bool TryParse(Type type, StringValues values, out object? result) + { + result = values + .SelectMany(value => value?.Split(' ', StringSplitOptions.RemoveEmptyEntries) + ?? Enumerable.Empty()) + .ToArray(); + + return true; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Configuration/CorsSettings.cs b/Abblix.Oidc.Server.Mvc/Configuration/CorsSettings.cs new file mode 100644 index 00000000..3df8b38b --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Configuration/CorsSettings.cs @@ -0,0 +1,64 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc.Configuration; + +/// +/// Represents the settings for Cross-Origin Resource Sharing (CORS) policy. +/// CORS policies allow web applications to make requests from different domains, +/// enhancing security by restricting cross-origin requests. +/// +public record CorsSettings +{ + /// + /// A list of origins that are allowed to make requests to the resource, + /// enhancing security by limiting cross-origin interactions. + /// + public string[]? AllowedOrigins { get; init; } + + /// + /// Specifies which HTTP methods are permitted when accessing the resource from the allowed origins. + /// + public string[]? AllowedMethods { get; init; } + + /// + /// Defines which HTTP headers can be used when making the actual request to the resource. + /// + public string[]? AllowedHeaders { get; init; } + + /// + /// Indicates whether the resource supports credentials like cookies, authorization headers, + /// or TLS client certificates. + /// + public bool? AllowCredentials { get; init; } + + /// + /// Specifies which headers can be exposed as part of the response by listing them. + /// + public string[]? ExposeHeaders { get; init; } + + /// + /// Defines the maximum duration the information provided by the Access-Control-Allow-Methods and + /// Access-Control-Allow-Headers headers can be cached. + /// + public TimeSpan? MaxAge { get; init; } +} diff --git a/Abblix.Oidc.Server.Mvc/Controllers/AuthenticationController.cs b/Abblix.Oidc.Server.Mvc/Controllers/AuthenticationController.cs new file mode 100644 index 00000000..221211f8 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Controllers/AuthenticationController.cs @@ -0,0 +1,236 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Endpoints.EndSession; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Abblix.Oidc.Server.Mvc.Model; +using Abblix.Oidc.Server.Mvc.Attributes; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; +using AuthorizationResponse = Abblix.Oidc.Server.Mvc.Model.AuthorizationResponse; +using UserInfoResponse = Abblix.Oidc.Server.Mvc.Model.UserInfoResponse; + +namespace Abblix.Oidc.Server.Mvc.Controllers; + +/// +/// Handles authentication-related processes in the context of OpenID Connect and OAuth2 protocols. +/// This controller manages user authorization, provides user information, handles end-session requests, +/// and checks session statuses. +/// +/// +/// This controller serves as the core component for managing user authentication and session control +/// in an OpenID Connect compliant manner. It includes endpoints for initiating user authorization, +/// retrieving authenticated user information, managing user logout processes, and checking the status +/// of user sessions for OIDC compliance. +/// +[ApiController] +[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] +[SkipStatusCodePages] +[RequireHttps] +public sealed class AuthenticationController : ControllerBase +{ + /// + /// Handles the pushed authorization endpoint. This endpoint is used for receiving and processing pushed authorization requests + /// from clients, validating the request, and generating a response that either contains a URI for the stored authorization request + /// or an error message. + /// + /// The handler responsible for processing pushed authorization requests. + /// The service for formatting the authorization response. + /// The authorization request received from the client. + /// Additional client request information for contextual validation. + /// An action result containing the formatted authorization response, which can be a success or an error response. + /// + /// This method first validates the incoming authorization request. If the request is valid, it is processed and stored, + /// and a response containing the request URI is returned. If the request is invalid, an error response is generated. + /// + [HttpPost(Path.PushAuthorizationRequest)] + [Consumes(MediaTypes.FormUrlEncoded)] + [Produces(MediaTypeNames.Text.Html, MediaTypeNames.Application.Json)] + public async Task> PushAuthorizeAsync( + [FromServices] IPushedAuthorizationHandler handler, + [FromServices] IPushedAuthorizationResponseFormatter formatter, + [FromForm] AuthorizationRequest authorizationRequest, + [FromForm] ClientRequest clientRequest) + { + var mappedAuthorizationRequest = authorizationRequest.Map(); + var mappedClientRequest = clientRequest.Map(); + var response = await handler.HandleAsync(mappedAuthorizationRequest, mappedClientRequest); + return await formatter.FormatResponseAsync(mappedAuthorizationRequest, response); + } + + /// + /// Handles requests to the authorization endpoint, performing user authentication and obtaining consent for requested scopes. + /// + /// The handler responsible for processing authorization requests. + /// The formatter used to generate a response for the authorization request. + /// The authorization request details received from the client. + /// A task that represents the asynchronous operation, resulting in an action result containing the authorization response. + /// + /// This endpoint is a key component of the OpenID Connect flow, initiating user authentication and consent for access to their information. + /// + /// OpenID Connect Authorization Endpoint Documentation + /// + /// + [HttpGetOrPost(Path.Authorize)] + //[Consumes(MediaTypes.FormUrlEncoded)] + [Produces(MediaTypeNames.Text.Html, MediaTypeNames.Application.Json)] + public async Task> AuthorizeAsync( + [FromServices] IAuthorizationHandler handler, + [FromServices] IAuthorizationResponseFormatter formatter, + [FromQueryOrForm] AuthorizationRequest request) + { + var authorizationRequest = request.Map(); + var response = await handler.HandleAsync(authorizationRequest); + return await formatter.FormatResponseAsync(authorizationRequest, response); + } + + /// + /// Processes requests to the userinfo endpoint, returning claims about the authenticated user based on the provided access token. + /// + /// The handler responsible for processing userinfo requests. + /// The formatter used to generate a response with user claims. + /// The userinfo request containing the access token. + /// Additional request information provided by the client. + /// A task that represents the asynchronous operation, resulting in an action result containing the userinfo response. + /// + /// This endpoint provides claims about the authenticated user, conforming to the + /// + /// OpenID Connect UserInfo Endpoint Documentation + /// + /// + [HttpGetOrPost(Path.UserInfo)] + [EnableCors(OidcConstants.CorsPolicyName)] + public async Task> UserInfoAsync( + [FromServices] IUserInfoHandler handler, + [FromServices] IUserInfoResponseFormatter formatter, + [FromQueryOrForm] UserInfoRequest userInfoRequest, + [FromQueryOrForm] ClientRequest clientRequest) + { + var mappedUserInfoRequest = userInfoRequest.Map(); + var mappedClientRequest = clientRequest.Map(); + var response = await handler.HandleAsync(mappedUserInfoRequest, mappedClientRequest); + return await formatter.FormatResponseAsync(mappedUserInfoRequest, response); + } + + /// + /// Facilitates the logout process by handling requests to the end session endpoint, allowing clients to terminate the user's session. + /// + /// The handler responsible for processing end session requests. + /// The formatter used to generate a response for the end session request. + /// The end session request details received from the client. + /// A task that represents the asynchronous operation, resulting in an action result for the end session process. + /// + /// This endpoint supports the RP-Initiated Logout functionality, enabling clients to initiate logout procedures compliant with OpenID Connect. + /// + /// OpenID Connect EndSession Endpoint Documentation + /// + /// + [HttpGetOrPost(Path.EndSession)] + //[Consumes(MediaTypes.FormUrlEncoded, IsOptional = true)] + [Produces(MediaTypeNames.Text.Html, MediaTypeNames.Application.Json)] + [EnableCors(OidcConstants.CorsPolicyName)] + public async Task EndSessionAsync( + [FromServices] IEndSessionHandler handler, + [FromServices] IEndSessionResponseFormatter formatter, + [FromQueryOrForm] EndSessionRequest request) + { + var endSessionRequest = request.Map(); + var response = await handler.HandleAsync(endSessionRequest); + return await formatter.FormatResponseAsync(endSessionRequest, response); + } + + /// + /// Monitors the user's session state by handling requests to the check session endpoint, typically used within an iframe for session management. + /// + /// The handler responsible for the check session operation. + /// The formatter used to generate a response suitable for session checking within an iframe. + /// A task that represents the asynchronous operation, resulting in an action result for the check session response. + /// + /// This endpoint is part of the OpenID Connect session management specification, enabling clients to monitor the authentication state. + /// + /// OpenID Connect Check Session Documentation + /// + /// + [HttpGet(Path.CheckSession)] + [Produces(MediaTypes.Javascript)] + [EnableCors(OidcConstants.CorsPolicyName)] + public async Task CheckSessionAsync( + [FromServices] ICheckSessionHandler handler, + [FromServices] ICheckSessionResponseFormatter formatter) + { + var response = await handler.HandleAsync(); + return await formatter.FormatResponseAsync(response); + } + + /*/// + /// Handles the backchannel authentication endpoint, initiating an out-of-band authentication process. + /// + /// + /// + /// CIBA - Client Initiated Backchannel Authentication Documentation + /// + /// + [HttpPost(Path.BackchannelAuthentication)] + [Consumes(MediaTypes.FormUrlEncoded)] + public async Task BackChannelAuthenticationAsync( + [FromServices] IBackChannelAuthenticationRequestValidator validator, + [FromServices] IBackChannelAuthenticationRequestProcessor processor, + [FromServices] IBackChannelAuthenticationResponseFormatter formatter, + [FromForm] BackChannelAuthenticationRequest request) + { + var backChannelAuthenticationRequest = request.Map(); + var validationResult = await validator.ValidateAsync(backChannelAuthenticationRequest); + + var response = validationResult switch + { + ValidBackChannelAuthenticationRequest validRequest => await processor.ProcessAsync(validRequest), + + BackChannelAuthenticationValidationError { Error: var error, ErrorDescription: var description } + => new BackChannelAuthenticationError(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + + return await formatter.FormatResponseAsync(backChannelAuthenticationRequest, response); + } + + /// + /// Handles the device authorization endpoint for obtaining user authorization on limited-input devices. + /// + /// + /// + /// OAuth 2.0 Device Authorization Grant Documentation + /// + /// + [HttpPost(Path.DeviceAuthorization)] + [Consumes(MediaTypes.FormUrlEncoded)] + public Task DeviceAuthorizationAsync() + { + throw new NotImplementedException(); + }*/ +} diff --git a/Abblix.Oidc.Server.Mvc/Controllers/ClientManagementController.cs b/Abblix.Oidc.Server.Mvc/Controllers/ClientManagementController.cs new file mode 100644 index 00000000..2756fada --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Controllers/ClientManagementController.cs @@ -0,0 +1,124 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Abblix.Oidc.Server.Mvc.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Controllers; + +/// +/// The ClientManagementController is responsible for handling client-related operations in the context of OAuth 2.0 and +/// OpenID Connect. This includes dynamic client registration, reading client configurations, and deleting registered clients. +/// +/// +/// The controller adheres to the OpenID Connect Dynamic Client Registration protocol, allowing clients to register themselves +/// dynamically with the authorization server. It supports operations like registering new clients, querying existing client configurations, +/// and removing clients. This is crucial for systems where client applications need to be managed programmatically without manual intervention. +/// For detailed protocol specifications, refer to . +/// +[ApiController] +[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] +[SkipStatusCodePages] +[RequireHttps] +public class ClientManagementController : ControllerBase +{ + /// + /// Registers a new client dynamically with the authorization server. This endpoint processes the client + /// registration requests by validating the provided details and creating a new client configuration. + /// + /// The handler responsible for processing client registration requests. + /// The formatter responsible for generating the client registration response. + /// The details of the client registration request. + /// A task that represents the asynchronous operation, resulting in an action result that includes + /// the client registration response. + /// + /// This method implements the OpenID Connect Dynamic Client Registration protocol, facilitating clients + /// to register dynamically. It validates the request, processes it if valid, and formats a response that + /// can include either the successful registration details or an error message. + /// + [HttpPost(Path.Register)] + [Produces(MediaTypeNames.Application.Json)] + [Consumes(MediaTypeNames.Application.Json)] + public async Task RegisterClientAsync( + [FromServices] IRegisterClientHandler handler, + [FromServices] IRegisterClientResponseFormatter formatter, + [FromBody] ClientRegistrationRequest request) + { + var clientRegistrationRequest = request.Map(); + var response = await handler.HandleAsync(clientRegistrationRequest); + return await formatter.FormatResponseAsync(clientRegistrationRequest, response); + } + + /// + /// Retrieves the configuration of a previously registered client from the authorization server. + /// + /// The handler responsible for processing client information requests. + /// The formatter responsible for generating the client information response. + /// The details of the client information request. + /// A task that represents the asynchronous operation, resulting in an action result that includes + /// the client information response. + /// + /// This method allows clients to query their current configuration stored by the authorization server. + /// It is compliant with the OpenID Connect Dynamic Client Registration protocol, enabling clients to manage + /// their registration details post-registration. + /// + [HttpGet(Path.Register)] + [Produces(MediaTypeNames.Application.Json)] + public async Task ReadClientAsync( + [FromServices] IReadClientHandler handler, + [FromServices] IReadClientResponseFormatter formatter, + [FromQuery] ClientRequest request) + { + var clientRequest = request.Map(); + var response = await handler.HandleAsync(clientRequest); + return await formatter.FormatResponseAsync(clientRequest, response); + } + + /// + /// Removes a registered client's configuration from the authorization server, effectively revoking its registration + /// and access. + /// + /// The handler responsible for processing client removal requests. + /// The formatter responsible for generating the client removal response. + /// The details of the client removal request. + /// A task that represents the asynchronous operation, resulting in an action result that confirms + /// the client removal. + /// + /// This method supports the removal of clients from the authorization server. Following the OpenID Connect Dynamic + /// Client Registration protocol, it allows for the clean-up of client configurations that are no longer needed + /// or valid. + /// + [HttpDelete(Path.Register)] + [Produces(MediaTypeNames.Application.Json)] + public async Task RemoveClientAsync( + [FromServices] IRemoveClientHandler handler, + [FromServices] IRemoveClientResponseFormatter formatter, + [FromQuery] ClientRequest request) + { + var clientRequest = request.Map(); + var response = await handler.HandleAsync(clientRequest); + return await formatter.FormatResponseAsync(clientRequest, response); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs b/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs new file mode 100644 index 00000000..f252c772 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs @@ -0,0 +1,177 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using System.Text.Json; +using System.Text.Json.Serialization; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Grants; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.LogoutNotification; +using Abblix.Oidc.Server.Features.UserInfo; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using JsonWebKeySet = Abblix.Jwt.JsonWebKeySet; + + +namespace Abblix.Oidc.Server.Mvc.Controllers; + +/// +/// The DiscoveryController handles requests for OpenID Connect Discovery, providing information about the OpenID Provider's configuration. +/// It supports endpoints for retrieving the provider's metadata and its public key information. +/// +/// +/// This controller implements the functionality described in the OpenID Connect Discovery 1.0 specification, +/// which enables clients to discover essential information about the OpenID Provider, such as its authorization +/// and token endpoints, supported scopes, response types, and more. +/// +/// This facilitates clients in dynamically configuring themselves to communicate with the OpenID Provider. +/// For more details, refer to the OpenID Connect Discovery specification: +/// . +/// +[ApiController] +[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] +[SkipStatusCodePages] +public sealed class DiscoveryController : ControllerBase +{ + /// + /// Handles the request for the OpenID Provider Configuration Information. This endpoint + /// returns crucial information about the OpenID Connect provider, such as the issuer, + /// key discovery URIs, supported scopes, response types, and more, facilitating dynamic + /// client configuration for OpenID Connect compliance. + /// + /// + /// The response adheres to the OpenID Connect Discovery specification, providing a + /// standardized set of information necessary for clients to interact with the provider. + /// + /// A task that results in an action result containing the provider's configuration details + /// in JSON format. + [HttpGet(Path.Configuration)] + [Produces(MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status200OK)] + public Task> ConfigurationAsync( + [FromServices] IOptionsSnapshot options, + [FromServices] IIssuerProvider issuerProvider, + [FromServices] IUriResolver uriResolver, + [FromServices] ILogoutNotifier logoutNotifier, + [FromServices] IClientAuthenticator clientAuthenticator, + [FromServices] IAuthorizationGrantHandler authorizationGrantHandler, + [FromServices] IScopeClaimsProvider scopeClaimsProvider, + [FromServices] IJsonWebTokenCreator jwtCreator, + [FromServices] IJsonWebTokenValidator jwtValidator, + [FromServices] IAuthorizationHandler authorizationHandler, + [FromServices] ISubjectTypeConverter subjectTypeConverter) + { + var response = new ConfigurationResponse + { + Issuer = LicenseChecker.CheckIssuer(issuerProvider.GetIssuer()), + + JwksUri = Resolve(Path.Keys, OidcEndpoints.Keys), + AuthorizationEndpoint = Resolve(Path.Authorize, OidcEndpoints.Authorize), + TokenEndpoint = Resolve(Path.Token, OidcEndpoints.Token), + UserInfoEndpoint = Resolve(Path.UserInfo, OidcEndpoints.UserInfo), + EndSessionEndpoint = Resolve(Path.EndSession, OidcEndpoints.EndSession), + CheckSessionIframe = Resolve(Path.CheckSession, OidcEndpoints.CheckSession), + RevocationEndpoint = Resolve(Path.Revocation, OidcEndpoints.Revocation), + IntrospectionEndpoint = Resolve(Path.Introspection, OidcEndpoints.Introspection), + RegistrationEndpoint = Resolve(Path.Register, OidcEndpoints.Register), + PushedAuthorizationRequestEndpoint = Resolve(Path.PushAuthorizationRequest, OidcEndpoints.PushedAuthorizationRequest), + + FrontChannelLogoutSupported = logoutNotifier.FrontChannelLogoutSupported, + FrontChannelLogoutSessionSupported = logoutNotifier.FrontChannelLogoutSessionSupported, + BackChannelLogoutSupported = logoutNotifier.BackChannelLogoutSupported, + BackChannelLogoutSessionSupported = logoutNotifier.BackChannelLogoutSessionSupported, + + ClaimsParameterSupported = authorizationHandler.Metadata.ClaimsParameterSupported, + + ScopesSupported = scopeClaimsProvider.ScopesSupported, + ClaimsSupported = scopeClaimsProvider.ClaimsSupported, + + GrantTypesSupported = authorizationGrantHandler.GrantTypesSupported, + + ResponseTypesSupported = authorizationHandler.Metadata.ResponseTypesSupported, + ResponseModesSupported = authorizationHandler.Metadata.ResponseModesSupported, + + TokenEndpointAuthMethodsSupported = clientAuthenticator.ClientAuthenticationMethodsSupported, + + SubjectTypesSupported = subjectTypeConverter.SubjectTypesSupported, + PromptValuesSupported = authorizationHandler.Metadata.PromptValuesSupported, + + CodeChallengeMethodsSupported = authorizationHandler.Metadata.CodeChallengeMethodsSupported, + + RequestParameterSupported = authorizationHandler.Metadata.RequestParameterSupported, + RequestObjectSigningAlgValuesSupported = authorizationHandler.Metadata.RequestParameterSupported + ? jwtValidator.SigningAlgValuesSupported + : null, + + IdTokenSigningAlgValuesSupported = jwtCreator.SigningAlgValuesSupported, + UserInfoSigningAlgValuesSupported = jwtCreator.SigningAlgValuesSupported, + }; + + return Task.FromResult>(Json(response)); + + Uri? Resolve(string contentPath, OidcEndpoints enablingFlag) + => options.Value.Discovery.AllowEndpointPathsDiscovery && options.Value.EnabledEndpoints.HasFlag(enablingFlag) + ? uriResolver.Content(contentPath) + : null; + } + + /// + /// Provides the public key information used by the OpenID Provider to sign tokens. + /// This endpoint returns a JSON Web Key Set (JWKS) containing the public keys used by the provider. + /// Clients can use these keys to verify the authenticity of identity tokens and access tokens issued by the provider. + /// + /// Options for OpenID Connect, containing configuration settings and enabled endpoints. + /// Provider for retrieving the service's public key information. + /// + /// A JSON Web Key Set (JWKS) response in the form of if the Keys endpoint is enabled, + /// containing the public keys used by the provider. The response conforms to the application/json media type. + /// If the Keys endpoint is disabled, a 404 Not Found response is returned. + /// + [HttpGet(Path.Keys)] + public async Task> KeysAsync( + [FromServices] IOptionsSnapshot options, + [FromServices] IAuthServiceKeysProvider serviceKeysProvider) + { + if (!options.Value.EnabledEndpoints.HasFlag(OidcEndpoints.Keys)) + return NotFound(); + + var keys = await serviceKeysProvider.GetSigningKeys().ToListAsync(); + return Json(new JsonWebKeySet(keys.ToArray())); + } + + private static JsonResult Json(object response) => new( + response, + new JsonSerializerOptions + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }); +} diff --git a/Abblix.Oidc.Server.Mvc/Controllers/TokenController.cs b/Abblix.Oidc.Server.Mvc/Controllers/TokenController.cs new file mode 100644 index 00000000..84fb2de0 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Controllers/TokenController.cs @@ -0,0 +1,140 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Mvc.Attributes; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Abblix.Oidc.Server.Mvc.Model; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; +using TokenResponse = Abblix.Oidc.Server.Mvc.Model.TokenResponse; + +namespace Abblix.Oidc.Server.Mvc.Controllers; + +/// +/// Manages OAuth 2.0 and OpenID Connect token-related endpoints, including token issuance, revocation, and introspection. +/// Serves as the primary interface between clients and the authorization server for managing tokens' lifecycle. +/// +[ApiController] +[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] +[SkipStatusCodePages] +[RequireHttps] +public class TokenController : ControllerBase +{ + /// + /// Processes token issuance requests by evaluating authorization grants and issuing access, ID and refresh tokens + /// accordingly. + /// + /// The service responsible for handling token issuance logic. + /// The service tasked with formatting the token issuance response. + /// Details of the token request, including grant type and required parameters. + /// Contextual information about the client making the request. + /// A task that results in an action containing the token response, formatted as per OIDC specifications. + /// + /// + /// This endpoint is central to the OAuth 2.0 and OpenID Connect framework, facilitating the secure issuance + /// of tokens to authenticated clients. + /// + /// OpenID Connect Token Endpoint Documentation + /// + /// + [HttpGetOrPost(Path.Token)] + //[Consumes(MediaTypes.FormUrlEncoded)] + [Produces(MediaTypeNames.Application.Json)] + [EnableCors(OidcConstants.CorsPolicyName)] + public async Task> TokenAsync( + [FromServices] ITokenHandler handler, + [FromServices] ITokenResponseFormatter formatter, + [FromQueryOrForm] TokenRequest tokenRequest, + [FromQueryOrForm] ClientRequest clientRequest) + { + var mappedTokenRequest = tokenRequest.Map(); + var mappedClientRequest = clientRequest.Map(); + var response = await handler.HandleAsync(mappedTokenRequest, mappedClientRequest); + return await formatter.FormatResponseAsync(mappedTokenRequest, response); + } + + /// + /// Facilitates the revocation of issued tokens, enhancing security by allowing clients to invalidate tokens + /// no longer needed or potentially compromised. + /// + /// The service responsible for processing token revocation requests. + /// The service responsible for formatting the revocation response. + /// Details of the revocation request, including the token to be revoked. + /// Additional contextual information about the client making the revocation request. + /// A task that results in an action indicating the outcome of the revocation process. + /// + /// Adheres to the OAuth 2.0 Token Revocation standard, allowing clients to manage the lifecycle of their tokens + /// securely. + /// OAuth 2.0 Token Revocation Documentation + /// + [HttpPost(Path.Revocation)] + [Consumes(MediaTypes.FormUrlEncoded)] + [EnableCors(OidcConstants.CorsPolicyName)] + public async Task RevocationAsync( + [FromServices] IRevocationHandler handler, + [FromServices] IRevocationResponseFormatter formatter, + [FromForm] RevocationRequest revocationRequest, + [FromForm] ClientRequest clientRequest) + { + var mappedRevocationRequest = revocationRequest.Map(); + var mappedClientRequest = clientRequest.Map(); + var response = await handler.HandleAsync(mappedRevocationRequest, mappedClientRequest); + return await formatter.FormatResponseAsync(mappedRevocationRequest, response); + } + + /// + /// Allows clients to query the state of a specific token, verifying its validity, active status, and other relevant + /// metadata. + /// + /// The service responsible for validating and processing token introspection requests. + /// The service responsible for formatting the introspection response with detailed token + /// information. + /// Details of the introspection request, including the token to be introspected. + /// + /// Additional contextual information about the client making the introspection request. + /// + /// A task that results in an action providing detailed information about the state of the queried token. + /// + /// + /// Implements the OAuth 2.0 Token Introspection specification, allowing clients to verify the status of tokens in + /// a secure manner. + /// OAuth 2.0 Token Introspection Documentation + /// + [HttpPost(Path.Introspection)] + [Consumes(MediaTypes.FormUrlEncoded)] + public async Task IntrospectionAsync( + [FromServices] IIntrospectionHandler handler, + [FromServices] IIntrospectionResponseFormatter formatter, + [FromForm] IntrospectionRequest introspectionRequest, + [FromForm] ClientRequest clientRequest) + { + var mappedIntrospectionRequest = introspectionRequest.Map(); + var mappedClientRequest = clientRequest.Map(); + var response = await handler.HandleAsync(mappedIntrospectionRequest, mappedClientRequest); + return await formatter.FormatResponseAsync(mappedIntrospectionRequest, response); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCache.cs b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCache.cs new file mode 100644 index 00000000..3ad14c3a --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCache.cs @@ -0,0 +1,62 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Mvc.Features.SessionManagement; + +/// +/// Represents a cache for storing and retrieving CheckSession responses asynchronously. +/// +/// +/// This cache is used to store CheckSession responses for quick retrieval, reducing the need to recompute them +/// for the same input key. +/// +public class CheckSessionResponseCache : ICheckSessionResponseCache +{ + /// + /// Initializes a new instance of the class with the specified cache options. + /// + /// The options to configure the memory cache. + public CheckSessionResponseCache(IOptions cacheOptions) + { + _cache = new MemoryCache(cacheOptions); + } + + private readonly MemoryCache _cache; + + /// + /// Gets an ActionResult from the cache with the specified key, or adds it to the cache if not present. + /// + /// The key used to identify the item in the cache. + /// + /// A delegate that provides the ActionResult to be added to the cache if it doesn't exist. + /// + /// + /// A representing the asynchronous operation. The task result contains + /// the cached or newly generated ActionResult. + /// + public Task GetOrAddAsync(object key, Func> factory) + => _cache.GetOrCreateAsync(key, _ => factory())!; +} diff --git a/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCachingDecorator.cs b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCachingDecorator.cs new file mode 100644 index 00000000..533a1c81 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/CheckSessionResponseCachingDecorator.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Features.SessionManagement; + +/// +/// A decorator class that adds caching functionality to the Check Session response formatting process. +/// +public class CheckSessionResponseCachingDecorator: ICheckSessionResponseFormatter +{ + /// + /// Initializes a new instance of the class. + /// + /// The inner Check Session response formatter. + /// The cache used to store formatted responses. + public CheckSessionResponseCachingDecorator( + ICheckSessionResponseFormatter inner, + ICheckSessionResponseCache cache) + { + _inner = inner; + _cache = cache; + } + + private readonly ICheckSessionResponseFormatter _inner; + private readonly ICheckSessionResponseCache _cache; + + /// + /// Formats a Check Session response and caches the formatted result using the provided response's cache key. + /// + /// The Check Session response to be formatted. + /// + /// A representing the asynchronous operation. The task result contains + /// the formatted ActionResult, either retrieved from the cache or generated by the inner formatter. + /// + public Task FormatResponseAsync(CheckSessionResponse response) + => _cache.GetOrAddAsync(response.CacheKey, () => _inner.FormatResponseAsync(response)); +} diff --git a/Abblix.Oidc.Server.Mvc/Features/SessionManagement/EndSessionResponseFormatterDecorator.cs b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/EndSessionResponseFormatterDecorator.cs new file mode 100644 index 00000000..905aa3d3 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/EndSessionResponseFormatterDecorator.cs @@ -0,0 +1,74 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Features.SessionManagement; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.ActionResults; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Features.SessionManagement; + +/// +/// A decorator class that adds session management functionality to the End Session response formatting process. +/// +public class EndSessionResponseFormatterDecorator: IEndSessionResponseFormatter +{ + /// + /// Initializes a new instance of the class. + /// + /// The inner End Session response formatter. + /// The service responsible for session management. + public EndSessionResponseFormatterDecorator( + IEndSessionResponseFormatter inner, + ISessionManagementService sessionManagementService) + { + _inner = inner; + _sessionManagementService = sessionManagementService; + } + + private readonly IEndSessionResponseFormatter _inner; + private readonly ISessionManagementService _sessionManagementService; + + /// + /// Formats an End Session response and performs session management operations if enabled. + /// + /// The End Session request. + /// The End Session response to be formatted. + /// + /// A representing the asynchronous operation. The task result contains + /// the formatted ActionResult, with additional session management actions if enabled. + /// + public async Task FormatResponseAsync(EndSessionRequest request, EndSessionResponse response) + { + var result = await _inner.FormatResponseAsync(request, response); + + if (_sessionManagementService.Enabled) + { + var cookie = _sessionManagementService.GetSessionCookie(); + result = result.WithDeleteCookie(cookie.Name, cookie.Options.ConvertOptions()); + } + + return result; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Features/SessionManagement/ICheckSessionResponseCache.cs b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/ICheckSessionResponseCache.cs new file mode 100644 index 00000000..73668716 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Features/SessionManagement/ICheckSessionResponseCache.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Features.SessionManagement; + +/// +/// Represents a cache for storing and retrieving Check Session response data. +/// +public interface ICheckSessionResponseCache +{ + /// + /// Gets an ActionResult from the cache with the specified key, or adds it to the cache if not present. + /// + /// The key used to identify the item in the cache. + /// + /// A delegate that provides the ActionResult to be added to the cache if it doesn't exist. + /// + /// + /// A representing the asynchronous operation. The task result contains + /// the cached or newly generated ActionResult. + /// + Task GetOrAddAsync(object key, Func> factory); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationErrorFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationErrorFormatter.cs new file mode 100644 index 00000000..560be8de --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationErrorFormatter.cs @@ -0,0 +1,123 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Binders; +using Abblix.Utils; +using Microsoft.AspNetCore.Mvc; +using AuthorizationResponse = Abblix.Oidc.Server.Mvc.Model.AuthorizationResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Handles the formatting of authorization error responses, converting them into an appropriate HTTP action +/// result based on the specified response mode and the nature of the error. +/// +public class AuthorizationErrorFormatter +{ + /// + /// Initializes a new instance of with the necessary parameter provider. + /// + /// The provider for extracting and formatting response parameters. + public AuthorizationErrorFormatter(IParametersProvider parametersProvider) + { + _parametersProvider = parametersProvider; + } + + private readonly IParametersProvider _parametersProvider; + + /// + /// Asynchronously formats an authorization error response into an HTTP action result, + /// considering the request's redirect URI and the error details. + /// + /// The original authorization request that led to the error. + /// The authorization error to be formatted. + /// A task that resolves to the formatted HTTP action result. + public Task FormatResponseAsync( + AuthorizationRequest request, + AuthorizationError error) + { + return Task.FromResult(FormatResponse(request, error)); + } + + /// + /// Internally formats the authorization error response based on the request context and the error's properties, + /// such as whether to use a redirect URI. + /// + /// The authorization request associated with the error. + /// The error to be formatted into a response. + /// The action result representing the formatted error response. + private ActionResult FormatResponse(AuthorizationRequest request, AuthorizationError error) + { + switch (error) + { + case { RedirectUri: not null }: + + var response = new AuthorizationResponse + { + Error = error.Error, + ErrorDescription = error.ErrorDescription, + ErrorUri = error.ErrorUri, + + State = request.State, + }; + + return ToActionResult(response, error.ResponseMode, error.RedirectUri); + + default: + return new BadRequestObjectResult(new ErrorResponse(error.Error, error.ErrorDescription)); + } + } + + /// + /// Converts an authorization response into the appropriate action result type based on the specified response mode. + /// + /// The authorization response to convert. + /// The response mode indicating how the response should be delivered. + /// The URI to redirect to, if applicable. + /// The action result for the given authorization response. + protected ActionResult ToActionResult(AuthorizationResponse response, string responseMode, Uri redirectUri) + { + return responseMode switch + { + ResponseModes.FormPost => new OkObjectResult(response) + { + Formatters = { new AutoPostFormatter(_parametersProvider, redirectUri) }, + }, + + ResponseModes.Query => new RedirectResult(redirectUri.AddToQuery(GetParametersFrom(response))), + ResponseModes.Fragment => new RedirectResult(redirectUri.AddToFragment(GetParametersFrom(response))), + + _ => throw new ArgumentOutOfRangeException(nameof(responseMode)), + }; + } + + /// + /// Extracts and formats response parameters from an authorization response. + /// + /// The authorization response containing the parameters. + /// An array of name-value pairs representing the response parameters. + private (string name, string? value)[] GetParametersFrom(AuthorizationResponse response) + => _parametersProvider.GetParameters(response).ToArray(); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationResponseFormatter.cs new file mode 100644 index 00000000..bf16322b --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/AuthorizationResponseFormatter.cs @@ -0,0 +1,186 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.SessionManagement; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.ActionResults; +using Abblix.Oidc.Server.Mvc.Binders; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Abblix.Utils; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using AuthorizationResponse = Abblix.Oidc.Server.Mvc.Model.AuthorizationResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Handles the formatting of authorization responses in compliance with OpenID Connect and OAuth 2.0 protocols. +/// This formatter is responsible for transforming internal authorization response models into appropriate +/// HTTP responses that can be understood by clients and end-users. +/// +internal class AuthorizationResponseFormatter : AuthorizationErrorFormatter, IAuthorizationResponseFormatter +{ + /// + /// Initializes a new instance of the class, setting up essential + /// services and configuration options needed to format authorization responses. + /// + /// The configuration options for OpenID Connect. + /// + /// The storage service for managing and retrieving authorization requests. + /// + /// The provider for retrieving additional parameters needed during the formatting process. + /// + /// The service responsible for managing user sessions within the authorization process. + /// + /// Accessor to obtain the current HTTP context, facilitating access to request and response objects. + + public AuthorizationResponseFormatter( + IOptions options, + IAuthorizationRequestStorage authorizationRequestStorage, + IParametersProvider parametersProvider, + ISessionManagementService sessionManagementService, + IHttpContextAccessor httpContextAccessor) + : base(parametersProvider) + { + _options = options; + _authorizationRequestStorage = authorizationRequestStorage; + _sessionManagementService = sessionManagementService; + _httpContextAccessor = httpContextAccessor; + } + + private readonly IAuthorizationRequestStorage _authorizationRequestStorage; + private readonly ISessionManagementService _sessionManagementService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IOptions _options; + + /// + /// Formats an authorization response based on the specified request and response model asynchronously. + /// It handles various outcomes such as redirections for additional user interactions and successful authentication, + /// adapting the response according to the OpenID Connect and OAuth 2.0 specifications. + /// + /// The authorization request containing details about the initial request from the client. + /// + /// The authorization response model to format. + /// A task that represents the asynchronous operation and results in an + /// that can be returned by an ASP.NET Core controller. + public async Task FormatResponseAsync( + AuthorizationRequest request, + Endpoints.Authorization.Interfaces.AuthorizationResponse response) + { + switch (response) + { + case AccountSelectionRequired: + return await RedirectAsync( + _options.Value.AccountSelectionUri.NotNull(nameof(OidcOptions.AccountSelectionUri)), response.Model); + + case ConsentRequired: + return await RedirectAsync( + _options.Value.ConsentUri.NotNull(nameof(OidcOptions.ConsentUri)), response.Model); + + case InteractionRequired: + return await RedirectAsync( + _options.Value.InteractionUri.NotNull(nameof(OidcOptions.InteractionUri)), response.Model); + + case LoginRequired: + return await RedirectAsync( + _options.Value.LoginUri.NotNull(nameof(OidcOptions.LoginUri)), response.Model); + + case SuccessfullyAuthenticated { Model.RedirectUri: not null } success: + + var modelResponse = new AuthorizationResponse + { + State = response.Model.State, + Scope = string.Join(' ', response.Model.Scope), + + Code = success.Code, + + TokenType = success.TokenType, + AccessToken = success.AccessToken?.EncodedJwt, + + IdToken = success.IdToken?.EncodedJwt, + + SessionState = success.SessionState, + }; + + var actionResult = ToActionResult(modelResponse, success.ResponseMode, response.Model.RedirectUri); + + if (_sessionManagementService.Enabled && + success.SessionId.HasValue() && + response.Model.Scope.Contains(Scopes.OpenId)) + { + var cookie = _sessionManagementService.GetSessionCookie(); + + actionResult = actionResult.WithAppendCookie( + cookie.Name, + success.SessionId, + cookie.Options.ConvertOptions()); + } + + return actionResult; + + case AuthorizationError error: + return await base.FormatResponseAsync(response.Model, error); + + default: + throw new UnexpectedTypeException(nameof(response), response.GetType()); + } + } + + /// + /// Helper method to redirect the user agent to a specified URI while attaching an authorization request. + /// + /// The base URI to redirect to. + /// The authorization request to attach to the URI as a query parameter. + /// A task that represents the asynchronous operation and results in a redirect action result. + private async Task RedirectAsync(Uri uri, AuthorizationRequest request) + { + var response = await _authorizationRequestStorage.StoreAsync( + request, + _options.Value.LoginSessionExpiresIn); + + if (!uri.IsAbsoluteUri) + { + var httpContext = _httpContextAccessor.HttpContext; + + var requestUri = new Uri( + httpContext.NotNull(nameof(httpContext)).Request.GetDisplayUrl(), + UriKind.Absolute); + + uri = new Uri(requestUri, uri); + } + + return new RedirectResult(new UriBuilder(uri) + { + Query = + { + [_options.Value.RequestUriParameterName] = response.RequestUri.OriginalString, + } + }); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/BackChannelAuthenticationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/BackChannelAuthenticationResponseFormatter.cs new file mode 100644 index 00000000..34e1af2a --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/BackChannelAuthenticationResponseFormatter.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Formats responses for back-channel authentication. +/// +public class BackChannelAuthenticationResponseFormatter : IBackChannelAuthenticationResponseFormatter +{ + /// + /// Formats a response for back-channel authentication asynchronously. + /// + /// The back-channel authentication request. + /// The back-channel authentication response to be formatted. + /// A representing the asynchronous operation, with the formatted response as an . + public Task FormatResponseAsync( + BackChannelAuthenticationRequest request, + BackChannelAuthenticationResponse response) + { + throw new NotImplementedException(); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/CheckSessionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/CheckSessionResponseFormatter.cs new file mode 100644 index 00000000..df67fbd4 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/CheckSessionResponseFormatter.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Mime; +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Provides a response formatter for Check Session frames. +/// +internal class CheckSessionResponseFormatter : ICheckSessionResponseFormatter +{ + /// + /// Formats a response for a Check Session frame asynchronously. + /// + /// The Check Session response containing HTML content. + /// A representing the asynchronous operation, with the formatted response as an . + public Task FormatResponseAsync(CheckSessionResponse response) + { + var content = response.HtmlContent; + + var result = new ContentResult + { + StatusCode = StatusCodes.Status200OK, + ContentType = MediaTypeNames.Text.Html, + Content = content, + }; + + return Task.FromResult(result); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/EndSessionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/EndSessionResponseFormatter.cs new file mode 100644 index 00000000..900d533d --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/EndSessionResponseFormatter.cs @@ -0,0 +1,78 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.ActionResults; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Provides a response formatter for end-session requests, which is part of the OpenID Connect protocol. +/// +public class EndSessionResponseFormatter : IEndSessionResponseFormatter +{ + /// + /// Formats an end-session response asynchronously based on the provided response model. + /// + /// The end-session request. + /// The end-session response model. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// + public Task FormatResponseAsync(EndSessionRequest request, EndSessionResponse response) + => Task.FromResult(FormatResponse(response)); + + private static ActionResult FormatResponse(EndSessionResponse response) + { + switch (response) + { + case EndSessionSuccessfulResponse success: + + ActionResult result; + if (success.FrontChannelLogoutRequestUris.Count > 0) + { + result = new FrontChannelLogoutResult( + success.PostLogoutRedirectUri, + success.FrontChannelLogoutRequestUris); + } + else if (success.PostLogoutRedirectUri != null) + { + result = new RedirectResult(success.PostLogoutRedirectUri.OriginalString); + } + else + { + result = new NoContentResult(); + } + + return result; + + case EndSessionErrorResponse { Error: var error, ErrorDescription: var description }: + return new BadRequestObjectResult(new ErrorResponse(error, description)); + + default: + throw new ArgumentOutOfRangeException(nameof(response)); + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IAuthorizationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IAuthorizationResponseFormatter.cs new file mode 100644 index 00000000..b8b1ab5f --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IAuthorizationResponseFormatter.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting an authorization response from a strong-typed model +/// to a framework-specific response. +/// +public interface IAuthorizationResponseFormatter +{ + /// + /// Formats an authorization response asynchronously, converting it from a strong-typed model to a framework-specific response. + /// + /// The authorization request. + /// The authorization response to be formatted. + /// A representing the asynchronous operation, with the formatted response. + Task FormatResponseAsync( + AuthorizationRequest request, + Endpoints.Authorization.Interfaces.AuthorizationResponse response); +} \ No newline at end of file diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IBackChannelAuthenticationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IBackChannelAuthenticationResponseFormatter.cs new file mode 100644 index 00000000..2106bdaa --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IBackChannelAuthenticationResponseFormatter.cs @@ -0,0 +1,45 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting a back-channel authentication response to be sent to a client or external system. +/// +public interface IBackChannelAuthenticationResponseFormatter +{ + /// + /// Formats a back-channel authentication response asynchronously. + /// + /// The back-channel authentication request. + /// The back-channel authentication response to be formatted. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// + Task FormatResponseAsync( + BackChannelAuthenticationRequest request, + BackChannelAuthenticationResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ICheckSessionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ICheckSessionResponseFormatter.cs new file mode 100644 index 00000000..bd8e4fed --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ICheckSessionResponseFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting a response to a low-level object with the content of an OpenID Provider (OP) check-session frame. +/// +public interface ICheckSessionResponseFormatter +{ + /// + /// Formats a response asynchronously to a low-level object with the content of an OpenID Provider (OP) check-session frame. + /// + /// The check-session response to be formatted. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// + Task FormatResponseAsync(CheckSessionResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IEndSessionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IEndSessionResponseFormatter.cs new file mode 100644 index 00000000..1446089a --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IEndSessionResponseFormatter.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting a response to an end-session request and response. +/// +public interface IEndSessionResponseFormatter +{ + /// + /// Formats a response asynchronously for an end-session request and response. + /// + /// The end-session request. + /// The end-session response to be formatted. + /// A representing the asynchronous operation, + /// with the formatted response as an . + Task FormatResponseAsync(EndSessionRequest request, EndSessionResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IIntrospectionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IIntrospectionResponseFormatter.cs new file mode 100644 index 00000000..7b126a55 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IIntrospectionResponseFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting an OAuth 2.0 introspection response document as a low-level response object to return it to the client. +/// +public interface IIntrospectionResponseFormatter +{ + /// + /// Formats an OAuth 2.0 introspection response document asynchronously as a low-level response object to return it to the client. + /// + /// The introspection request. + /// The introspection response to be formatted. + /// A representing the asynchronous operation, with the formatted response as an . + Task FormatResponseAsync(IntrospectionRequest request, IntrospectionResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IPushedAuthorizationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IPushedAuthorizationResponseFormatter.cs new file mode 100644 index 00000000..0d5fe5fc --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IPushedAuthorizationResponseFormatter.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines the interface for formatting responses to pushed authorization requests. +/// +public interface IPushedAuthorizationResponseFormatter +{ + /// + /// Formats the response to a pushed authorization request. + /// + /// The original authorization request. + /// The response from processing the authorization request, + /// which could be a successful pushed authorization response or an error. + /// A task that resolves to an action result suitable for returning from an MVC action, + /// representing the formatted response. + Task FormatResponseAsync( + AuthorizationRequest request, + Endpoints.Authorization.Interfaces.AuthorizationResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IReadClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IReadClientResponseFormatter.cs new file mode 100644 index 00000000..67c5e4d3 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IReadClientResponseFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting a response when reading client information as a low-level response object to return it to the client. +/// +public interface IReadClientResponseFormatter +{ + /// + /// Formats a response asynchronously when reading client information as a low-level response object to return it to the client. + /// + /// The client information request. + /// The response containing client information to be formatted. + /// A representing the asynchronous operation, with the formatted response as an . + Task FormatResponseAsync(ClientRequest request, ReadClientResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRegisterClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRegisterClientResponseFormatter.cs new file mode 100644 index 00000000..befacad8 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRegisterClientResponseFormatter.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; +using ClientRegistrationResponse = + Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces.ClientRegistrationResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting client registration responses. +/// +public interface IRegisterClientResponseFormatter +{ + /// + /// Formats the given client registration response asynchronously. + /// + /// The original client registration request. + /// The client registration response to format. + /// A representing the asynchronous operation. + /// The task result contains the formatted . + Task FormatResponseAsync(ClientRegistrationRequest request, ClientRegistrationResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRemoveClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRemoveClientResponseFormatter.cs new file mode 100644 index 00000000..13545600 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRemoveClientResponseFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting responses when removing a client. +/// +public interface IRemoveClientResponseFormatter +{ + /// + /// Formats the response asynchronously when removing a client. + /// + /// The original request to remove the client. + /// The response indicating the result of the removal operation. + /// A representing the asynchronous operation. The task result contains the formatted . + Task FormatResponseAsync(ClientRequest request, RemoveClientResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRevocationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRevocationResponseFormatter.cs new file mode 100644 index 00000000..5b4db984 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IRevocationResponseFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Model; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting a revocation response as a low-level response object to return to the client. +/// +public interface IRevocationResponseFormatter +{ + /// + /// Formats a revocation response asynchronously as a low-level response object to return to the client. + /// + /// The revocation request. + /// The revocation response to be formatted. + /// A representing the asynchronous operation, with the formatted response as an . + Task FormatResponseAsync(RevocationRequest request, RevocationResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ITokenResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ITokenResponseFormatter.cs new file mode 100644 index 00000000..aed3befc --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/ITokenResponseFormatter.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Mvc.Model; +using Microsoft.AspNetCore.Mvc; +using TokenRequest = Abblix.Oidc.Server.Model.TokenRequest; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting an OAuth 2.0 token response as a low-level response object to return to the client. +/// +public interface ITokenResponseFormatter +{ + /// + /// Formats an OAuth 2.0 token response asynchronously as a low-level response object to return to the client. + /// + /// The token request. + /// The token response to be formatted. + /// A representing the asynchronous operation, with the formatted response as an containing a . + Task> FormatResponseAsync(TokenRequest request, + Endpoints.Token.Interfaces.TokenResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IUserInfoResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IUserInfoResponseFormatter.cs new file mode 100644 index 00000000..03322e4e --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/Interfaces/IUserInfoResponseFormatter.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Mvc.Model; +using Microsoft.AspNetCore.Mvc; +using UserInfoRequest = Abblix.Oidc.Server.Model.UserInfoRequest; + +namespace Abblix.Oidc.Server.Mvc.Formatters.Interfaces; + +/// +/// Defines an interface for formatting an OpenID Connect UserInfo response as a low-level response object to return to the client. +/// +public interface IUserInfoResponseFormatter +{ + /// + /// Formats an OpenID Connect UserInfo response asynchronously as a low-level response object to return to the client. + /// + /// The UserInfo request. + /// The UserInfo response to be formatted. + /// A representing the asynchronous operation, with the formatted response as an . + Task> FormatResponseAsync(UserInfoRequest request, + Endpoints.UserInfo.Interfaces.UserInfoResponse response); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/IntrospectionResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/IntrospectionResponseFormatter.cs new file mode 100644 index 00000000..d131ce48 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/IntrospectionResponseFormatter.cs @@ -0,0 +1,69 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Jwt; +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Provides a response formatter for introspection responses. +/// +public class IntrospectionResponseFormatter : IIntrospectionResponseFormatter +{ + /// + /// Formats an introspection response asynchronously. + /// + /// The introspection request. + /// The introspection response model. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// + /// + /// This method is used to format introspection responses. + /// Depending on the response type, it creates different types of ActionResult to be returned to the client. + /// + public Task FormatResponseAsync(IntrospectionRequest request, IntrospectionResponse response) + { + return Task.FromResult(response switch + { + IntrospectionSuccessResponse success => Format(success), + + IntrospectionErrorResponse error => + new UnauthorizedObjectResult(new ErrorResponse(error.Error, error.ErrorDescription)), + + _ => throw new ArgumentOutOfRangeException(nameof(response)) + }); + } + + private static ActionResult Format(IntrospectionSuccessResponse success) + { + var result = success.Claims ?? new JsonObject(); + result.SetProperty("active", success.Active ? "true" : "false"); + + return new JsonResult(result); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/PushedAuthorizationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/PushedAuthorizationResponseFormatter.cs new file mode 100644 index 00000000..e39c8280 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/PushedAuthorizationResponseFormatter.cs @@ -0,0 +1,83 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Binders; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Implements response formatting for pushed authorization requests, extending the base functionality to handle +/// specific response types. +/// +public class PushedAuthorizationResponseFormatter : AuthorizationErrorFormatter, IPushedAuthorizationResponseFormatter +{ + /// + /// Initializes a new instance of the class + /// with the specified parameters provider. + /// + /// Provides access to parameters used in formatting the response. + public PushedAuthorizationResponseFormatter(IParametersProvider parametersProvider) + : base(parametersProvider) + { + } + + /// + /// Asynchronously formats the response to a pushed authorization request. + /// + /// The original authorization request. + /// The response from processing the authorization request. + /// This could be a indicating success, + /// or an indicating failure. + /// A task that resolves to an action result suitable for returning from an MVC action, + /// representing the formatted response. This could include setting specific HTTP status codes + /// or returning error information. + public async Task FormatResponseAsync( + AuthorizationRequest request, + AuthorizationResponse response) + { + switch (response) + { + case PushedAuthorizationResponse par: + + var modelResponse = new Model.PushedAuthorizationResponse + { + RequestUri = par.RequestUri, + ExpiresIn = par.ExpiresIn, + }; + + return new JsonResult(modelResponse) { StatusCode = StatusCodes.Status201Created }; + + case AuthorizationError error: + return await base.FormatResponseAsync(request, error); + + default: + throw new UnexpectedTypeException(nameof(response), response.GetType()); + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/ReadClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/ReadClientResponseFormatter.cs new file mode 100644 index 00000000..1d43c136 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/ReadClientResponseFormatter.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Provides a response formatter for reading client responses. +/// +public class ReadClientResponseFormatter : IReadClientResponseFormatter +{ + /// + /// Formats a response for reading a client asynchronously. + /// + /// The client request. + /// The client response model. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// + /// + /// This method is used to format the response for reading a client. + /// Depending on the response type, it creates different types of ActionResult to be returned to the client. + /// + public Task FormatResponseAsync(ClientRequest request, ReadClientResponse response) + { + return Task.FromResult(response switch + { + ReadClientSuccessfulResponse success => new OkObjectResult(success), + ReadClientErrorResponse error => new NotFoundObjectResult(error), + _ => throw new UnexpectedTypeException(nameof(response), response.GetType()), + }); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/RegisterClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/RegisterClientResponseFormatter.cs new file mode 100644 index 00000000..967f3b0c --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/RegisterClientResponseFormatter.cs @@ -0,0 +1,114 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Controllers; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Abblix.Utils; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using ClientRegistrationResponse = Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces.ClientRegistrationResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Provides a response formatter for client registration responses. +/// +public class RegisterClientResponseFormatter : IRegisterClientResponseFormatter +{ + /// + /// Initializes a new instance of the class. + /// + /// The action URI provider used for generating URIs for client management actions. + public RegisterClientResponseFormatter(IUriResolver uriResolver) + { + _uriResolver = uriResolver; + } + + private readonly IUriResolver _uriResolver; + + /// + /// Formats a client registration response asynchronously, converting the response model to an appropriate + /// based on the nature of the response. + /// + /// The client registration request containing the data submitted by the client. + /// The client registration response model to be formatted. + /// + /// A representing the asynchronous operation, with the formatted response as an . + /// Depending on the response, this may be a success result with client details or an error response. + /// + /// + /// This method handles different types of responses: successful registration and error scenarios. + /// In the case of successful registration, it returns a 201 Created response with client details. + /// In the case of an error, it returns a 400 Bad Request response with error details. + /// + public Task FormatResponseAsync( + ClientRegistrationRequest request, + ClientRegistrationResponse response) + { + return Task.FromResult(FormatResponse(request, response)); + } + + private ActionResult FormatResponse(ClientRegistrationRequest request, ClientRegistrationResponse response) + { + switch (response) + { + case ClientRegistrationSuccessResponse success: + + var modelResponse = new Abblix.Oidc.Server.Model.ClientRegistrationResponse + { + ClientId = success.ClientId, + ClientIdIssuedAt = success.ClientIdIssuedAt, + + ClientSecret = success.ClientSecret, + ClientSecretExpiresAt = success.ClientSecretExpiresAt ?? DateTimeOffset.UnixEpoch, + + RegistrationAccessToken = success.RegistrationAccessToken, + + RegistrationClientUri = success.RegistrationAccessToken.HasValue() + ? GetClientReadUrl(success.ClientId) + : null, + + InitiateLoginUri = request.InitiateLoginUri + }; + + return new ObjectResult(modelResponse) { StatusCode = StatusCodes.Status201Created }; + + case ClientRegistrationErrorResponse error: + return new BadRequestObjectResult(new ErrorResponse(error.Error, error.ErrorDescription)); + + default: + throw new UnexpectedTypeException(nameof(response), response.GetType()); + } + } + + private Uri GetClientReadUrl(string clientId) => _uriResolver.Action( + MvcUtils.TrimAsync(nameof(ClientManagementController.ReadClientAsync)), + MvcUtils.NameOf(), + new RouteValueDictionary + { + { ClientRequest.Parameters.ClientId, clientId }, + }); +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/RemoveClientResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/RemoveClientResponseFormatter.cs new file mode 100644 index 00000000..b7bbb73f --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/RemoveClientResponseFormatter.cs @@ -0,0 +1,65 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Formatter for responses to client removal requests. +/// +public class RemoveClientResponseFormatter : IRemoveClientResponseFormatter +{ + /// + /// Asynchronously formats the response for a client removal request. + /// + /// The client request. + /// The response to the client removal request. + /// + /// A task that represents the asynchronous operation. The task result contains the formatted action result. + /// + public Task FormatResponseAsync(ClientRequest request, RemoveClientResponse response) + => Task.FromResult(FormatResponse(response)); + + /// + /// Formats the response for a client removal request. + /// + /// The response to the client removal request. + /// The formatted action result. + /// Thrown when the response type is unexpected. + private static ActionResult FormatResponse(RemoveClientResponse response) + { + return response switch + { + RemoveClientSuccessfulResponse => new NoContentResult(), + + RemoveClientErrorResponse { Error: var error, ErrorDescription: var description } + => new BadRequestObjectResult(new ErrorResponse(error, description)), + + _ => throw new UnexpectedTypeException(nameof(response), response.GetType()) + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/RevocationResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/RevocationResponseFormatter.cs new file mode 100644 index 00000000..db3fa09e --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/RevocationResponseFormatter.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Formatter for responses to token revocation requests. +/// +public class RevocationResponseFormatter : IRevocationResponseFormatter +{ + /// + /// Asynchronously formats the response for a token revocation request. + /// + /// + /// This method handles different types of revocation responses and formats them + /// into appropriate HTTP action results. + /// + /// The token revocation request. + /// The response to the token revocation request. + /// + /// A task that represents the asynchronous operation. The task result contains the formatted action result. + /// + public Task FormatResponseAsync(RevocationRequest request, RevocationResponse response) + { + return Task.FromResult(response switch + { + TokenRevokedResponse => new OkResult(), + RevocationErrorResponse error => new BadRequestObjectResult(new ErrorResponse(error.Error, + error.ErrorDescription)), + _ => throw new ArgumentOutOfRangeException(nameof(response)) + }); + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/TokenResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/TokenResponseFormatter.cs new file mode 100644 index 00000000..14d2bdae --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/TokenResponseFormatter.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; +using TokenResponse = Abblix.Oidc.Server.Mvc.Model.TokenResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Formatter for token responses. +/// +public class TokenResponseFormatter : ITokenResponseFormatter +{ + /// + /// Asynchronously formats the response for a token request. + /// + /// The token request. + /// The response from the token endpoint. + /// + /// A task that represents the asynchronous operation. The task result contains the formatted token response. + /// + public Task> FormatResponseAsync( + TokenRequest request, + Endpoints.Token.Interfaces.TokenResponse response) + { + return Task.FromResult(FormatResponse(response)); + } + + /// + /// Formats the response from the token endpoint. + /// + /// The response from the token endpoint. + /// + /// The formatted token response as an . + /// + private static ActionResult FormatResponse(Endpoints.Token.Interfaces.TokenResponse response) + { + switch (response) + { + case TokenIssuedResponse success: + //TODO: append headers 'Cache-Control: no-store' and 'Pragma: no-cache' to response - as requires https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse + return new TokenResponse + { + AccessToken = success.AccessToken.EncodedJwt, + TokenType = success.TokenType, + IssuedTokenType = success.IssuedTokenType, + ExpiresIn = success.ExpiresIn, + + RefreshToken = success.RefreshToken?.EncodedJwt, + IdToken = success.IdToken?.EncodedJwt, + }; + + case TokenErrorResponse error: + return new BadRequestObjectResult(error); + + default: + throw new ArgumentOutOfRangeException(nameof(response)); + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/Formatters/UserInfoResponseFormatter.cs b/Abblix.Oidc.Server.Mvc/Formatters/UserInfoResponseFormatter.cs new file mode 100644 index 00000000..da441796 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Formatters/UserInfoResponseFormatter.cs @@ -0,0 +1,109 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Mvc; +using UserInfoResponse = Abblix.Oidc.Server.Mvc.Model.UserInfoResponse; + +namespace Abblix.Oidc.Server.Mvc.Formatters; + +/// +/// Formatter for user information responses. +/// +public class UserInfoResponseFormatter : IUserInfoResponseFormatter +{ + /// + /// Initializes a new instance of the class. + /// + /// Provides the current time. + /// Formats JWTs for clients. + public UserInfoResponseFormatter( + TimeProvider clock, + IClientJwtFormatter clientJwtFormatter) + { + _clock = clock; + _clientJwtFormatter = clientJwtFormatter; + } + + private readonly IClientJwtFormatter _clientJwtFormatter; + private readonly TimeProvider _clock; + + /// + /// Asynchronously formats the response for a user information request. + /// + /// + /// This method handles different types of user information responses and formats them + /// into appropriate HTTP action results. + /// + /// The user information request. + /// The response from the user information endpoint. + /// + /// A task that represents the asynchronous operation. The task result contains the formatted user information response. + /// + public async Task> FormatResponseAsync( + UserInfoRequest request, + Endpoints.UserInfo.Interfaces.UserInfoResponse response) + { + switch (response) + { + case UserInfoFoundResponse + { + ClientInfo: var clientInfo, + User: var user, + Issuer: var issuer, + } + when clientInfo.UserInfoSignedResponseAlgorithm != SigningAlgorithms.None: + + var token = new JsonWebToken + { + Header = { Algorithm = clientInfo.UserInfoSignedResponseAlgorithm }, + Payload = new JsonWebTokenPayload(user) + { + Issuer = issuer, + IssuedAt = _clock.GetUtcNow(), + Audiences = new[] { clientInfo.ClientId }, + } + }; + + return new ContentResult + { + ContentType = MediaTypes.Jwt, + Content = await _clientJwtFormatter.FormatAsync(token, clientInfo), + }; + + case UserInfoFoundResponse { User: var user }: + return new JsonResult(user); + + case UserInfoErrorResponse error: + return new BadRequestObjectResult(error); + + default: + throw new UnexpectedTypeException(nameof(response), response.GetType()); + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/GlobalUsings.cs b/Abblix.Oidc.Server.Mvc/GlobalUsings.cs new file mode 100644 index 00000000..140a4b81 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/GlobalUsings.cs @@ -0,0 +1,25 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +global using UriBuilder = Abblix.Utils.UriBuilder; +global using AllowedValuesAttribute = Abblix.Oidc.Server.Mvc.Attributes.AllowedValuesAttribute; +global using ElementsRequiredAttribute = Abblix.Oidc.Server.Mvc.Attributes.ElementsRequiredAttribute; diff --git a/Abblix.Oidc.Server.Mvc/HttpRequestExtensions.cs b/Abblix.Oidc.Server.Mvc/HttpRequestExtensions.cs new file mode 100644 index 00000000..03d469eb --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/HttpRequestExtensions.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Http; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides extension methods for the class. +/// These methods are used to retrieve various URL components from an HTTP request. +/// +public static class HttpRequestExtensions +{ + /// + /// Gets the application's base URL from the HTTP request. + /// This includes the scheme, host, and the base path of the application. + /// + /// The HTTP request. + /// The application's base URL. + public static string GetAppUrl(this HttpRequest request) => request.GetFullUrl(request.PathBase); + + /// + /// Gets the base URL of the request. + /// This includes the scheme, host, and the path of the request. + /// + /// The HTTP request. + /// The base URL of the request. + public static string GetBaseUrl(this HttpRequest request) => request.GetFullUrl(request.Path); + + /// + /// Constructs a full URL from the request's components and the specified path. + /// + /// The HTTP request. + /// The path to append to the base URL. + /// The full URL constructed from the request's components and the specified path. + private static string GetFullUrl(this HttpRequest request, PathString path) + => request.Scheme + Uri.SchemeDelimiter + request.Host + path; +} diff --git a/Abblix.Oidc.Server.Mvc/HttpRequestInfoAdapter.cs b/Abblix.Oidc.Server.Mvc/HttpRequestInfoAdapter.cs new file mode 100644 index 00000000..d21d2a16 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/HttpRequestInfoAdapter.cs @@ -0,0 +1,78 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Utils; +using Microsoft.AspNetCore.Http; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides detailed information about the current HTTP request in an ASP.NET Core application. +/// Implements to encapsulate the retrieval of request-specific data, +/// such as URLs and HTTPS status, facilitating access to these details throughout the application. +/// +public class HttpRequestInfoAdapter : IRequestInfoProvider +{ + /// + /// Initializes a new instance of the class. + /// + /// Accessor to obtain the . + public HttpRequestInfoAdapter(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + private readonly IHttpContextAccessor _httpContextAccessor; + + /// + /// Gets the representing the current HTTP request. + /// + private HttpRequest Request + { + get + { + var httpContext = _httpContextAccessor.HttpContext; + return httpContext.NotNull(nameof(httpContext)).Request; + } + } + + /// + /// The URI of the current request. + /// + public string RequestUri => Request.GetBaseUrl(); + + /// + /// The base URI of the application. + /// + public string ApplicationUri => Request.GetAppUrl(); + + /// + /// Indicates whether the current request is using HTTPS. + /// + public bool IsHttps => Request.IsHttps; + + /// + /// The base path of the request. + /// + public string PathBase => Request.PathBase; +} diff --git a/Abblix.Oidc.Server.Mvc/IUriResolver.cs b/Abblix.Oidc.Server.Mvc/IUriResolver.cs new file mode 100644 index 00000000..33f3d0dc --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/IUriResolver.cs @@ -0,0 +1,62 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides functionality for generating URIs for controller actions and static content within an application. +/// +public interface IUriResolver +{ + /// + /// Generates an absolute URI for a specified controller action, enabling action methods to be easily referenced + /// across the application for things like redirects or link generation. + /// + /// The name of the action method within the controller. + /// The name of the controller that contains the action method. + /// An object containing the route values for the action method. These values are used to + /// construct the query string and populate parameters in the route template. This parameter is optional. + /// An absolute URI as a object for the specified action within the controller. + /// + /// + /// var uri = uriResolver.Action("Index", "Home", new { id = 42 }); + /// // Result could be something like: "http://example.com/Home/Index?id=42" + /// + /// + Uri Action(string actionName, string controllerName, object? routeValues = null); + + /// + /// Generates an absolute URI for a given content path. This method is useful for creating links to static resources + /// stored within the application, such as images, CSS files, and JavaScript files. It supports application + /// root-relative paths using the '~' symbol. + /// + /// The path to the content. This can be a virtual path (e.g., "~/images/logo.png") indicating + /// the application root or a relative path from the current executing location. + /// An absolute URI as a object for the specified content path. + /// + /// + /// var uri = uriResolver.Content("~/content/site.css"); + /// // Result could be something like: "http://example.com/content/site.css" + /// + /// + Uri Content(string path); +} diff --git a/Abblix.Oidc.Server.Mvc/Model/AuthorizationRequest.cs b/Abblix.Oidc.Server.Mvc/Model/AuthorizationRequest.cs new file mode 100644 index 00000000..eca2b4ed --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/AuthorizationRequest.cs @@ -0,0 +1,224 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.DeclarativeValidation; +using Abblix.Oidc.Server.Model; +using Abblix.Oidc.Server.Mvc.Binders; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.AuthorizationRequest.Parameters; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents an authorization request model containing the necessary properties +/// for an OpenID Connect or OAuth 2.0 authorization process. +/// +public record AuthorizationRequest +{ + /// + /// Identifies the permissions the application is requesting from the user. + /// The specific scopes requested determine the access privileges granted. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Scope)] + [AllowedValues(Scopes.OpenId, Scopes.Profile, Scopes.Email, Scopes.Phone, Scopes.Address, Scopes.OfflineAccess)] + [ModelBinder(typeof(SpaceSeparatedValuesBinder))] + public string[] Scope { get; init; } = Array.Empty(); + + /// + /// Specifies the information about the user that the application seeks to access, + /// such as user profile data or email. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Claims)] + [ModelBinder(typeof(JsonSerializerModelBinder))] + public RequestedClaims? Claims { get; init; } + + /// + /// Indicates the type of response desired by the client, such as an authorization code + /// or an identity token. + /// + [BindProperty(SupportsGet = true, Name = Parameters.ResponseType)] + [AllowedValues(ResponseTypes.Code, ResponseTypes.Token, ResponseTypes.IdToken)] + [ModelBinder(typeof(SpaceSeparatedValuesBinder))] + public string[]? ResponseType { get; init; } + + /// + /// Unique identifier of the client application making the authorization request. + /// + [BindProperty(SupportsGet = true, Name = Parameters.ClientId)] + public string? ClientId { get; init; } = default!; + + /// + /// URL to which the response from the authorization request should be sent. + /// + [BindProperty(SupportsGet = true, Name = Parameters.RedirectUri)] + [AbsoluteUri] + public Uri? RedirectUri { get; init; } + + /// + /// A value used by the client to maintain state between the request and callback, + /// helping to keep the external session and to prevent Cross-Site Request Forgery attacks. + /// + [BindProperty(SupportsGet = true, Name = Parameters.State)] + public string? State { get; init; } + + /// + /// Specifies the mechanism for returning parameters from the authorization endpoint, + /// such as in the query string or fragment of the redirect URI. + /// + [BindProperty(SupportsGet = true, Name = Parameters.ResponseMode)] + [AllowedValues(ResponseModes.FormPost, ResponseModes.Fragment, ResponseModes.Query)] + public string? ResponseMode { get; init; } + + /// + /// A string value used to associate a client session with an ID token. + /// It is used to mitigate replay attacks. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Nonce)] + public string? Nonce { get; init; } + + /// + /// Influences the user interface of the authorization server, + /// suggesting how the authentication and consent UI should be displayed to the user. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Display)] + [AllowedValues(DisplayModes.Page, DisplayModes.Popup, DisplayModes.Touch, DisplayModes.Wap)] + public string? Display { get; init; } + + /// + /// Controls the behavior of the authorization server in terms of reauthentication and consent. + /// It can instruct the server to prompt the user for reauthentication or consent. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Prompt)] + [AllowedValues(Prompts.Create, Prompts.Consent, Prompts.Login, Prompts.None, Prompts.SelectAccount)] + public string? Prompt { get; init; } + + /// + /// Specifies the maximum allowable elapsed time since the last user authentication, + /// enabling clients to enforce reauthentication of users as needed. + /// + [BindProperty(SupportsGet = true, Name = Parameters.MaxAge)] + [ModelBinder(typeof(SecondsToTimeSpanModelBinder))] + public TimeSpan? MaxAge { get; init; } + + /// + /// Represents the preferred locales for the user interface, allowing clients to request + /// localization of the UI based on the user's preferences or language settings. + /// + [BindProperty(SupportsGet = true, Name = Parameters.UiLocales)] + [ModelBinder(typeof(CultureInfoBinder))] + public CultureInfo[]? UiLocales { get; init; } + + /// + /// Represents the preferred locales for claims, allowing clients to request + /// localization of claim values based on the user's preferences or language settings. + /// + [BindProperty(SupportsGet = true, Name = Parameters.ClaimsLocales)] + [ModelBinder(typeof(CultureInfoBinder))] + public CultureInfo[]? ClaimsLocales { get; init; } + + /// + /// Can be used to pass an ID token hint to pre-fill or bypass the authentication + /// and consent UI for a returning user. + /// + [BindProperty(SupportsGet = true, Name = Parameters.IdTokenHint)] + public string? IdTokenHint { get; init; } + + /// + /// Provides a hint to the authorization server about the login identifier the user might use. + /// + [BindProperty(SupportsGet = true, Name = Parameters.LoginHint)] + public string? LoginHint { get; init; } + + /// + /// Specifies the Authentication Context Class References, + /// enabling clients to request certain levels of authentication assurance. + /// + [BindProperty(SupportsGet = true, Name = Parameters.AcrValues)] + [ModelBinder(typeof(SpaceSeparatedValuesBinder))] + public string[]? AcrValues { get; init; } + + /// + /// Used in the PKCE (Proof Key for Code Exchange) extension for public clients, + /// providing a challenge derived from the code verifier that will be sent in the token request. + /// + [BindProperty(SupportsGet = true, Name = Parameters.CodeChallenge)] + public string? CodeChallenge { get; init; } + + /// + /// Indicates the method used to derive the code challenge, + /// supporting enhanced security for public clients in the PKCE flow. + /// + [BindProperty(SupportsGet = true, Name = Parameters.CodeChallengeMethod)] + [AllowedValues(CodeChallengeMethods.Plain, CodeChallengeMethods.S256)] + public string? CodeChallengeMethod { get; init; } + + /// + /// Encapsulates the authorization request parameters in a JWT format, + /// providing an additional layer of request integrity and confidentiality. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Request)] + public string? Request { get; init; } + + /// + /// References a resource containing a Request Object value, + /// allowing the use of external references for complex authorization requests. + /// + [BindProperty(SupportsGet = true, Name = Parameters.RequestUri)] + [AbsoluteUri(RequireScheme = "https")] + public Uri? RequestUri { get; init; } + + /// + /// Specifies the resource for which the access token is requested. + /// As defined in RFC 8707, this parameter is used to request access tokens with a specific scope for a particular + /// resource. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Resource)] + public Uri[]? Resource { get; set; } + + public Core.AuthorizationRequest Map() => new() + { + Nonce = Nonce, + Claims = Claims, + Display = Display, + Prompt = Prompt, + Scope = Scope, + State = State, + AcrValues = AcrValues, + ClientId = ClientId, + CodeChallenge = CodeChallenge, + LoginHint = LoginHint, + MaxAge = MaxAge, + RedirectUri = RedirectUri, + Request = Request, + RequestUri = RequestUri, + ResponseMode = ResponseMode, + ResponseType = ResponseType, + UiLocales = UiLocales, + CodeChallengeMethod = CodeChallengeMethod, + IdTokenHint = IdTokenHint, + ClaimsLocales = ClaimsLocales, + Resource = Resource, + }; +} diff --git a/Abblix.Oidc.Server.Mvc/Model/AuthorizationResponse.cs b/Abblix.Oidc.Server.Mvc/Model/AuthorizationResponse.cs new file mode 100644 index 00000000..ebd63b23 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/AuthorizationResponse.cs @@ -0,0 +1,117 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents an authorization response containing the result of an authorization request. +/// +public record AuthorizationResponse +{ + private static class Parameters + { + public const string State = "state"; + + public const string Code = "code"; + + public const string TokenType = "token_type"; + public const string AccessToken = "access_token"; + + public const string IdToken = "id_token"; + + public const string Error = "error"; + public const string ErrorDescription = "error_description"; + public const string ErrorUri = "error_uri"; + + public const string Scope = "scope"; + public const string SessionState = "session_state"; + } + + /// + /// The error code if the authorization request has failed. + /// + [JsonPropertyName(Parameters.Error)] + public string? Error { init; get; } + + /// + /// The human-readable description of the error if the authorization request has failed. + /// + [JsonPropertyName(Parameters.ErrorDescription)] + public string? ErrorDescription { init; get; } + + /// + /// The URI for more information about the error if the authorization request has failed. + /// + [JsonPropertyName(Parameters.ErrorUri)] + public Uri? ErrorUri { init; get; } + + /// + /// The state parameter that was included in the initial authorization request. + /// + [JsonPropertyName(Parameters.State)] + public string? State { init; get; } + + /// + /// The authorization code generated by the authorization server. + /// This code is used in the authorization code flow of OAuth 2.0 to obtain an access token. + /// + [JsonPropertyName(Parameters.Code)] + public string? Code { get; set; } + + /// + /// The OAuth 2.0 Token Type value. + /// The value is typically 'Bearer', but it can be another token_type value negotiated with the Authorization Server. + /// This is included when an access_token is issued. + /// + [JsonPropertyName(Parameters.TokenType)] + public string? TokenType { get; init; } + + /// + /// The access token issued by the authorization server. + /// Access tokens are credentials used to access protected resources. + /// + [JsonPropertyName(Parameters.AccessToken)] + public string? AccessToken { get; set; } + + /// + /// The ID token issued by the authorization server in OpenID Connect flows. + /// It contains claims about the authentication of an end-user. + /// + [JsonPropertyName(Parameters.IdToken)] + public string? IdToken { get; set; } + + /// + /// The scopes granted by the authorization server. + /// These scopes determine the level of access granted to the access token. + /// + [JsonPropertyName(Parameters.Scope)] + public string? Scope { get; set; } + + /// + /// The session state, which may be used in OpenID Connect session management + /// to maintain state between the client and the authorization server. + /// + [JsonPropertyName(Parameters.SessionState)] + public string? SessionState { get; set; } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/BackChannelAuthenticationRequest.cs b/Abblix.Oidc.Server.Mvc/Model/BackChannelAuthenticationRequest.cs new file mode 100644 index 00000000..9c97aa04 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/BackChannelAuthenticationRequest.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Core = Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a back-channel authentication request in the context of OAuth 2.0 or OpenID Connect. +/// This record extends from and is used to capture and map the necessary +/// parameters for initiating a back-channel authentication flow. +/// +public record BackChannelAuthenticationRequest : ClientRequest +{ + /// + /// Maps the properties of this back-channel authentication request to a corresponding + /// object. This mapping facilitates + /// the processing of the request in the core layers of the authentication framework. + /// + /// + /// A object populated with the relevant + /// data from this request. + /// + public new Core.BackChannelAuthenticationRequest Map() + { + return new Core.BackChannelAuthenticationRequest + { + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/ClientRegistrationRequest.cs b/Abblix.Oidc.Server.Mvc/Model/ClientRegistrationRequest.cs new file mode 100644 index 00000000..079fbdc7 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/ClientRegistrationRequest.cs @@ -0,0 +1,394 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.DeclarativeValidation; +using Abblix.Oidc.Server.Mvc.Binders; +using Abblix.Utils.Json; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.ClientRegistrationRequest.Parameters; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents metadata for an OAuth2 client based on the OpenID Connect discovery specification. +/// This class defines the properties required for registering a client with an OpenID Connect provider. +/// +/// +/// This follows the specification detailed at: https://openid.net/specs/openid-connect-registration-1_0.html +/// +public record ClientRegistrationRequest +{ + /// + /// Array of URIs to which the OpenID Provider will redirect the User Agent after obtaining authorization. + /// These URIs are used in the client's authorization request and are crucial for the redirect flow in OAuth2 and OpenID Connect. + /// + [JsonPropertyName(Parameters.RedirectUris)] + [Required] + [ElementsRequired] + public Uri[] RedirectUris { get; set; } = default!; + + /// + /// JSON array containing a list of the OAuth 2.0 response_type values. + /// + [JsonPropertyName(Parameters.ResponseTypes)] + [AllowedValues( + Common.Constants.ResponseTypes.Code, + Common.Constants.ResponseTypes.Token, + Common.Constants.ResponseTypes.IdToken)] + [JsonConverter(typeof(ArrayConverter))] + public string[][] ResponseTypes { get; init; } = { new[] { Common.Constants.ResponseTypes.Code } }; + + /// + /// Array of response type strings indicating the type of responses the client wishes to receive. + /// + [JsonPropertyName(Parameters.GrantTypes)] + [AllowedValues( + Common.Constants.GrantTypes.AuthorizationCode, + Common.Constants.GrantTypes.Implicit, + Common.Constants.GrantTypes.RefreshToken)] + public string[] GrantTypes { get; init; } = { Common.Constants.GrantTypes.AuthorizationCode }; + + /// + /// Specifies the type of the client application, such as 'web' or 'native'. + /// This affects how the client is expected to interact with the authorization server. + /// + [JsonPropertyName(Parameters.ApplicationType)] + public string ApplicationType { get; init; } = ApplicationTypes.Web; + + /// + /// Array of email addresses of people responsible for this client. + /// Useful for administrative contact in case of issues or updates. + /// + [JsonPropertyName(Parameters.Contacts)] + public string[]? Contacts { get; init; } + + /// + /// Custom identifier desired by the client. + /// It is primarily used for administrative purposes and client identification. + /// + [JsonPropertyName(Parameters.ClientId)] + public string? ClientId { get; init; } + + /// + /// Name of the client application. This is used for display purposes on consent screens + /// and in administrative interfaces. + /// + [JsonPropertyName(Parameters.ClientName)] + public string? ClientName { get; init; } + + /// + /// URL referencing the client's logo. This can be used in UIs like consent screens + /// to represent the client application. + /// + [JsonPropertyName(Parameters.LogoUri)] + [AbsoluteUri] + public Uri? LogoUri { get; init; } + + /// + /// URL of the client's home page. This can provide end-users with more information about the client application. + /// + [JsonPropertyName(Parameters.ClientUri)] + [AbsoluteUri] + public Uri? ClientUri { get; init; } + + /// + /// URL to the client's privacy policy. This provides end-users with information on how their data is handled by the client. + /// + [JsonPropertyName(Parameters.PolicyUri)] + [AbsoluteUri] + public Uri? PolicyUri { get; init; } + + /// + /// URL to the terms of service for the client. This is important for informing end-users about the terms + /// they are agreeing to by using the client application. + /// + [JsonPropertyName(Parameters.TosUri)] + [AbsoluteUri] + public Uri? TosUri { get; init; } + + /// + /// URL for the client's JSON Web Key Set (JWKS) document. This URI is where the OpenID Provider can retrieve + /// the client's public keys. + /// + [JsonPropertyName(Parameters.JwksUri)] + [AbsoluteUri] + public Uri? JwksUri { get; init; } + + /// + /// Client's JSON Web Key Set document value containing the public keys used by the client to sign requests and tokens. + /// This is an alternative to providing a JWKS URI. + /// + [JsonPropertyName(Parameters.Jwks)] + public JsonWebKeySet? Jwks { get; init; } + + /// + /// URL using the HTTPS scheme, identifying the client's sector identifier for pairwise subject identifiers. + /// This is used by the OpenID Provider to calculate consistent pairwise subject identifiers for the client. + /// + [JsonPropertyName(Parameters.SectorIdentifierUri)] + [AbsoluteUri] + public Uri? SectorIdentifierUri { get; init; } + + /// + /// Subject type requested for the client's ID Tokens. This can be 'public' for shared subject identifiers + /// or 'pairwise' for unique identifiers per client. + /// + [JsonPropertyName(Parameters.SubjectType)] + public string? SubjectType { get; init; } = SubjectTypes.Public; + + /// + /// JSON Web Signature (JWS) algorithm required for signing the ID Token issued to this client. + /// Specifies the algorithm that the OpenID Provider should use to sign ID Tokens sent to this client. + /// + [JsonPropertyName(Parameters.IdTokenSignedResponseAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? IdTokenSignedResponseAlg { get; init; } + + /// + /// JSON Web Encryption (JWE) algorithm required for encrypting the ID Token issued to this client. + /// Specifies the algorithm that must be used for encrypting ID Tokens. + /// + [JsonPropertyName(Parameters.IdTokenEncryptedResponseAlg)] + public string? IdTokenEncryptedResponseAlg { get; init; } + + /// + /// JWE encryption method required to encrypt the ID Token issued to this client. + /// Specifies the encryption method that must be used for encrypting ID Tokens. + /// + [JsonPropertyName(Parameters.IdTokenEncryptedResponseEnc)] + public string? IdTokenEncryptedResponseEnc { get; init; } + + /// + /// JSON Web Signature (JWS) algorithm required for UserInfo Responses. + /// Indicates the preferred algorithm for signing UserInfo responses sent to this client. + /// + [JsonPropertyName(Parameters.UserInfoSignedResponseAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? UserInfoSignedResponseAlg { get; init; } + + /// + /// JSON Web Encryption (JWE) algorithm required for encrypting UserInfo Responses. + /// Specifies the algorithm that must be used for encrypting UserInfo responses sent to this client. + /// + [JsonPropertyName(Parameters.UserInfoEncryptedResponseAlg)] + public string? UserInfoEncryptedResponseAlg { get; init; } + + /// + /// JWE encryption method required for encrypting the UserInfo Responses sent to this client. + /// Specifies the encryption method that must be used for encrypting UserInfo responses. + /// + [JsonPropertyName(Parameters.UserInfoEncryptedResponseEnc)] + public string? UserInfoEncryptedResponseEnc { get; init; } + + /// + /// JSON Web Signature (JWS) algorithm that MUST be used for Request Objects sent to the OP. + /// Specifies the client's preferred algorithm for signing Request Objects. + /// + [JsonPropertyName(Parameters.RequestObjectSigningAlg)] + public string? RequestObjectSigningAlg { get; init; } + + /// + /// JSON Web Encryption (JWE) algorithm the RP declares it may use for encrypting Request Objects sent to the OP. + /// Indicates the algorithm the client may use to encrypt Request Objects. + /// + [JsonPropertyName(Parameters.RequestObjectEncryptionAlg)] + public string? RequestObjectEncryptionAlg { get; init; } + + /// + /// JWE encryption method the RP declares it may use for encrypting Request Objects sent to the OP. + /// Specifies the encryption method the client may use for encrypting Request Objects. + /// + [JsonPropertyName(Parameters.RequestObjectEncryptionEnc)] + public string? RequestObjectEncryptionEnc { get; init; } + + /// + /// Requested Authentication Method Reference values for this client. + /// Specifies how the client will authenticate to the Token Endpoint. + /// + [JsonPropertyName(Parameters.TokenEndpointAuthMethod)] + [AllowedValues( + ClientAuthenticationMethods.ClientSecretBasic, + ClientAuthenticationMethods.ClientSecretPost, + ClientAuthenticationMethods.ClientSecretJwt, + ClientAuthenticationMethods.PrivateKeyJwt, + ClientAuthenticationMethods.None)] + public string TokenEndpointAuthMethod { get; init; } = ClientAuthenticationMethods.ClientSecretBasic; + + /// + /// JSON Web Signature (JWS) algorithm that MUST be used for Private Key JWT Client Authentication at the Token + /// Endpoint. Specifies the algorithm to be used by the client for signing JWTs used in client authentication. + /// + [JsonPropertyName(Parameters.TokenEndpointAuthSigningAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? TokenEndpointAuthSigningAlg { get; init; } + + /// + /// Default maximum duration in seconds for which the authentication is considered valid. + /// This value influences the expiry time of tokens issued to the client. + /// + [JsonPropertyName(Parameters.DefaultMaxAge)] + [ModelBinder(typeof(SecondsToTimeSpanModelBinder))] + public TimeSpan? DefaultMaxAge { get; init; } + + /// + /// Boolean flag indicating whether the OpenID Provider must include the `auth_time` claim in the ID token. + /// This is useful for clients that need to know the exact time of the user's authentication. + /// + [JsonPropertyName(Parameters.RequireAuthTime)] + public bool? RequireAuthTime { get; init; } + + /// + /// Default Authentication Context Class Reference values for this client. + /// These values indicate the preferred level of authentication assurance for the client. + /// + [JsonPropertyName(Parameters.DefaultAcrValues)] + [ModelBinder(typeof(SpaceSeparatedValuesBinder))] + public string[]? DefaultAcrValues { get; init; } + + /// + /// URI to initiate the login process for the client. + /// This URI triggers the authentication flow from a third-party location. + /// + [JsonPropertyName(Parameters.InitiateLoginUri)] + [AbsoluteUri] + public Uri? InitiateLoginUri { get; init; } + + /// + /// Array of URIs indicating where request parameters can be obtained. + /// These URIs point to resources containing request parameters in JWT format. + /// + [JsonPropertyName(Parameters.RequestUris)] + public Uri[]? RequestUris { get; init; } + + /// + /// Indicates whether Proof Key for Code Exchange (PKCE) is required for this client. + /// PKCE enhances the security of the OAuth authorization code flow, particularly for public clients. + /// + [JsonPropertyName(Parameters.PkceRequired)] + public bool PkceRequired { get; set; } + + /// + /// Indicates whether this client is allowed to request offline access. + /// This typically allows the client to receive refresh tokens, enabling long-term access without user interaction. + /// + [JsonPropertyName(Parameters.OfflineAccessAllowed)] + public bool OfflineAccessAllowed { get; set; } = true; + + /// + /// Indicates whether a back-channel logout session is required for this client. + /// This is relevant for scenarios where the client needs to be notified when the user logs out. + /// + [JsonPropertyName(Parameters.BackChannelLogoutSessionRequired)] + public bool BackChannelLogoutSessionRequired { get; set; } + + /// + /// URI used for back-channel logout. This URI is called by the OpenID Provider to initiate a logout for the client. + /// + [JsonPropertyName(Parameters.BackChannelLogoutUri)] + [AbsoluteUri] + public Uri? BackChannelLogoutUri { get; set; } + + /// + /// URI used for front-channel logout. This URI is used to log out the user through the user agent. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutUri)] + [AbsoluteUri] + public Uri? FrontChannelLogoutUri { get; set; } + + /// + /// Indicates whether the front-channel logout requires a session identifier. + /// This is used to manage user sessions in scenarios involving multiple clients. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutSessionRequired)] + public bool FrontChannelLogoutSessionRequired { get; set; } + + /// + /// Array of URIs to which the OP will redirect the user's user agent after logging out. + /// These URIs are used to continue the user's browsing session after logout. + /// + [JsonPropertyName(Parameters.PostLogoutRedirectUris)] + [ElementsRequired] + public Uri[] PostLogoutRedirectUris { get; set; } = Array.Empty(); + + /// + /// Maps the properties of this client registration request to a + /// object. This method is used to translate the request data into a format that can be processed by the core + /// registration logic. + /// + /// A object populated with data from this request. + public Core.ClientRegistrationRequest Map() + { + return new Core.ClientRegistrationRequest + { + Contacts = Contacts, + ClientId = ClientId, + Jwks = Jwks, + ResponseTypes = ResponseTypes, + ApplicationType = ApplicationType, + ClientName = ClientName, + GrantTypes = GrantTypes, + ClientUri = ClientUri, + JwksUri = JwksUri, + LogoUri = LogoUri, + PkceRequired = PkceRequired, + PolicyUri = PolicyUri, + RedirectUris = RedirectUris, + RequestUris = RequestUris, + SubjectType = SubjectType, + TermsOfServiceUri = TosUri, + DefaultAcrValues = DefaultAcrValues, + DefaultMaxAge = DefaultMaxAge, + InitiateLoginUri = InitiateLoginUri, + OfflineAccessAllowed = OfflineAccessAllowed, + RequireAuthTime = RequireAuthTime, + SectorIdentifierUri = SectorIdentifierUri, + + RequestObjectEncryptionAlg = RequestObjectEncryptionAlg, + RequestObjectEncryptionEnc = RequestObjectEncryptionEnc, + RequestObjectSigningAlg = RequestObjectSigningAlg, + + IdTokenEncryptedResponseAlg = IdTokenEncryptedResponseAlg, + IdTokenEncryptedResponseEnc = RequestObjectEncryptionEnc, + IdTokenSignedResponseAlg = IdTokenSignedResponseAlg, + + TokenEndpointAuthMethod = TokenEndpointAuthMethod, + TokenEndpointAuthSigningAlg = TokenEndpointAuthSigningAlg, + + UserInfoEncryptedResponseAlg = UserInfoEncryptedResponseAlg, + UserInfoEncryptedResponseEnc = UserInfoEncryptedResponseEnc, + UserInfoSignedResponseAlg = UserInfoSignedResponseAlg, + + BackChannelLogoutUri = BackChannelLogoutUri, + BackChannelLogoutSessionRequired = BackChannelLogoutSessionRequired, + + FrontChannelLogoutUri = FrontChannelLogoutUri, + FrontChannelLogoutSessionRequired = FrontChannelLogoutSessionRequired, + + PostLogoutRedirectUris = PostLogoutRedirectUris, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/ClientRequest.cs b/Abblix.Oidc.Server.Mvc/Model/ClientRequest.cs new file mode 100644 index 00000000..c3efe648 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/ClientRequest.cs @@ -0,0 +1,92 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Headers; +using Abblix.Oidc.Server.Mvc.Binders; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.ClientRequest.Parameters; +using HttpRequestHeaders = Abblix.Oidc.Server.Common.Constants.HttpRequestHeaders; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents an abstract model of a request made by a client via server-to-server call. +/// Contains all headers and properties that can be used for authentication in various possible ways. +/// This model captures essential information required for authenticating client requests in server-to-server interactions. +/// +public record ClientRequest +{ + /// + /// The authorization header value, typically containing credentials to authenticate the client to the server. + /// This header is used in standard HTTP authentication schemes like Basic, Bearer, etc. + /// + [ModelBinder(typeof(AuthenticationHeaderBinder), Name = HttpRequestHeaders.Authorization)] + public AuthenticationHeaderValue? AuthorizationHeader { get; set; } + + /// + /// The client identifier issued to the client during the registration process. + /// This identifier is unique to the client and is used in conjunction with the client secret to authenticate the client. + /// + [BindProperty(Name = Parameters.ClientId)] + public string? ClientId { get; set; } + + /// + /// The client secret that is known only to the client and the authorization server. + /// It is used in combination with the client ID to authenticate the client. + /// + [BindProperty(Name = Parameters.ClientSecret)] + public string? ClientSecret { get; set; } + + /// + /// The type of assertion being used for client authentication. + /// This property specifies the format of the client assertion used for authentication, such as JWT or SAML. + /// + [BindProperty(Name = Parameters.ClientAssertionType)] + public string? ClientAssertionType { get; set; } + + /// + /// The assertion that the client uses for authentication. + /// This property contains the actual assertion data, usually a digitally signed token, validating the client's identity. + /// + [BindProperty(Name = Parameters.ClientAssertion)] + public string? ClientAssertion { get; set; } + + /// + /// Maps the properties of this client request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server. + /// + /// A object populated with data from this request. + public Core.ClientRequest Map() + { + return new Core.ClientRequest + { + AuthorizationHeader = AuthorizationHeader, + + ClientId = ClientId, + ClientSecret = ClientSecret, + + ClientAssertionType = ClientAssertionType, + ClientAssertion = ClientAssertion + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/EndSessionRequest.cs b/Abblix.Oidc.Server.Mvc/Model/EndSessionRequest.cs new file mode 100644 index 00000000..ed66c59b --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/EndSessionRequest.cs @@ -0,0 +1,106 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using Abblix.Oidc.Server.Mvc.Binders; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.EndSessionRequest.Parameters; + + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a request to end a session, typically used in OpenID Connect logout scenarios. +/// This record encapsulates the necessary parameters for initiating a user logout request. +/// +public record EndSessionRequest +{ + /// + /// The ID token previously issued by the server, used as a hint about the End-User's current authenticated session. + /// This is typically used to ensure logout requests are associated with the correct user session. + /// + [BindProperty(SupportsGet = true, Name = Parameters.IdTokenHint)] + public string? IdTokenHint { get; set; } + + /// + /// A hint about the End-User's login identifier used by the client to request logout. + /// This can be useful for OpenID Providers in determining the relevant user session to end. + /// + [BindProperty(SupportsGet = true, Name = Parameters.LogoutHint)] + public string? LogoutHint { get; set; } + + /// + /// The client identifier for the application requesting the logout. + /// This helps the server in identifying which client application is initiating the logout process. + /// + [BindProperty(SupportsGet = true, Name = Parameters.ClientId)] + public string? ClientId { get; set; } + + /// + /// A value used by the client to maintain state between the logout request and callback. + /// This can be used to restore the client to the same state it was in before the logout was initiated. + /// + [BindProperty(SupportsGet = true, Name = Parameters.State)] + public string? State { get; set; } + + /// + /// URI where the user agent is redirected after a logout has been performed. + /// This URI allows the client to direct the user back to a specific location after logout. + /// + [BindProperty(SupportsGet = true, Name = Parameters.PostLogoutRedirectUri)] + public Uri? PostLogoutRedirectUri { get; set; } + + /// + /// Represents the UI locales preferred by the user for the logout page. + /// This allows clients to request localization of the logout UI based on user's preferences. + /// + [BindProperty(SupportsGet = true, Name = Parameters.UiLocales)] + [ModelBinder(typeof(CultureInfoBinder))] + public CultureInfo[]? UiLocales { get; set; } + + /// + /// Indicates whether the logout request has been confirmed by the user. + /// This is typically used to prevent accidental logouts and ensure intentional user actions. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Confirmed)] + public bool Confirmed { get; set; } = false; + + /// + /// Maps the properties of this end session request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server. + /// + /// A object populated with data from this request. + public Core.EndSessionRequest Map() + { + return new Core.EndSessionRequest + { + ClientId = ClientId, + UiLocales = UiLocales, + IdTokenHint = IdTokenHint, + State = State, + LogoutHint = LogoutHint, + PostLogoutRedirectUri = PostLogoutRedirectUri, + Confirmed = Confirmed, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/IntrospectionRequest.cs b/Abblix.Oidc.Server.Mvc/Model/IntrospectionRequest.cs new file mode 100644 index 00000000..54497c5a --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/IntrospectionRequest.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.IntrospectionRequest.Parameters; + + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a request for token introspection, extending from . +/// This record is used to determine the active state and meta-information about a token. +/// +public record IntrospectionRequest : ClientRequest +{ + /// + /// The token that the client wants to introspect. + /// This is the actual string value of the token for which the introspection request is being made. + /// + [BindProperty(Name = Parameters.Token)] + [Required] + public string Token { get; set; } = default!; + + /// + /// A hint about the type of the token submitted for introspection. + /// This can help the introspection endpoint optimize the token lookup. + /// If not provided, the endpoint may try various token types until it finds one that matches. + /// + [BindProperty(Name = Parameters.TokenTypeHint)] + public string? TokenTypeHint { get; set; } // TODO consider to use enum here + + /// + /// Maps the properties of this introspection request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server. + /// + /// A object populated with data from this request. + public new Core.IntrospectionRequest Map() + { + return new Core.IntrospectionRequest + { + Token = Token, + TokenTypeHint = TokenTypeHint, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/PushedAuthorizationResponse.cs b/Abblix.Oidc.Server.Mvc/Model/PushedAuthorizationResponse.cs new file mode 100644 index 00000000..29ce9f00 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/PushedAuthorizationResponse.cs @@ -0,0 +1,65 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; +using Abblix.Utils.Json; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents the response to a pushed authorization request. This response includes the URI +/// where the authorization request is stored and the duration for which the request will remain valid. +/// +public record PushedAuthorizationResponse +{ + /// + /// Defines constant parameter names used in the response. + /// + public static class Parameters { + /// + /// The parameter name for the request URI in the response. + /// + public const string RequestUri = "request_uri"; + + /// + /// The parameter name for the expiration duration of the stored request. + /// + public const string ExpiresIn = "expires_in"; + } + + /// + /// The URI where the authorization request is stored. + /// This URI is used by the client to refer to the authorization request in subsequent operations. + /// + [JsonPropertyName(Parameters.RequestUri)] + [JsonPropertyOrder(1)] + public Uri RequestUri { get; init; } = default!; + + /// + /// The duration for which the authorization request is considered valid. + /// After this period, the request may no longer be retrievable or usable. + /// + [JsonPropertyName(Parameters.ExpiresIn)] + [JsonConverter(typeof(TimeSpanSecondsConverter))] + [JsonPropertyOrder(2)] + public TimeSpan ExpiresIn { get; init; } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/RevocationRequest.cs b/Abblix.Oidc.Server.Mvc/Model/RevocationRequest.cs new file mode 100644 index 00000000..f04192be --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/RevocationRequest.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.RevocationRequest.Parameters; + + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a request for token revocation, extending from . +/// This record is used to invalidate a token, making it no longer usable for authorization purposes. +/// +public record RevocationRequest : ClientRequest +{ + /// + /// The token that the client wants to revoke. + /// This is the actual string value of the token which is intended to be invalidated and discontinued for further use. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Token)] + [Required] + public string Token { get; set; } = default!; + + /// + /// A hint about the type of the token submitted for revocation. + /// Providing this information can help the revocation endpoint handle the token more efficiently. + /// If omitted, the server may assume a default token type. + /// + [BindProperty(SupportsGet = true, Name = Parameters.TokenTypeHint)] + public string? TokenTypeHint { get; set; } // TODO consider to use enum here + + /// + /// Maps the properties of this revocation request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server. + /// + /// A object populated with data from this request. + public new Core.RevocationRequest Map() + { + return new Core.RevocationRequest + { + Token = Token, + TokenTypeHint = TokenTypeHint, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/TokenRequest.cs b/Abblix.Oidc.Server.Mvc/Model/TokenRequest.cs new file mode 100644 index 00000000..6fd463b9 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/TokenRequest.cs @@ -0,0 +1,135 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Mvc.Binders; +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.TokenRequest.Parameters; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a request for an OAuth 2.0 token, encapsulates various parameters used in different grant types +/// for obtaining tokens. +/// +public record TokenRequest +{ + /// + /// Specifies the OAuth 2.0 grant type of the token request. + /// This property defines the mechanism used to obtain the access token, such as authorization code, client credentials, or refresh token. + /// + [BindProperty(SupportsGet = true, Name = Parameters.GrantType)] + [Required] + [AllowedValues( + GrantTypes.AuthorizationCode, + GrantTypes.RefreshToken, + GrantTypes.Password, + GrantTypes.Ciba, + GrantTypes.Implicit, + GrantTypes.ClientCredentials)] + public string GrantType { get; set; } = default!; + + /// + /// The authorization code received from the authorization server. + /// This is used in the authorization code grant type to exchange the code for an access token. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Code)] + public string? Code { get; set; } + + /// + /// The URI where the client will be redirected after authorization. + /// This is used in conjunction with the authorization code grant type. + /// + [BindProperty(SupportsGet = true, Name = Parameters.RedirectUri)] + public Uri? RedirectUri { get; set; } + + /// + /// Specifies the resource for which the access token is requested. + /// As defined in RFC 8707, this parameter is used to request access tokens with a specific scope for a particular resource. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Resource)] + public Uri[]? Resource { get; set; } + + /// + /// The refresh token used to obtain a new access token. + /// This is applicable in scenarios where the client already holds a refresh token and requires a new access token. + /// + [BindProperty(SupportsGet = true, Name = Parameters.RefreshToken)] + public string? RefreshToken { get; set; } + + /// + /// Array of scope values indicating the permissions the client is requesting. + /// Scopes specify the level of access required and the associated permissions. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Scope)] + [AllowedValues( + Scopes.OpenId, + Scopes.Profile, + Scopes.Email, + Scopes.Phone, + Scopes.OfflineAccess)] + [ModelBinder(typeof(SpaceSeparatedValuesBinder))] + public string[] Scope { get; set; } = Array.Empty(); + + /// + /// The username of the resource owner, used in the password grant type. + /// This represents the credentials of the user for whom the client is requesting the token. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Username)] + public string? UserName { get; set; } + + /// + /// The password of the resource owner, used in the password grant type. + /// Along with the username, this forms the user credentials required for the password grant type. + /// + [BindProperty(SupportsGet = true, Name = Parameters.Password)] + public string? Password { get; set; } + + /// + /// The code verifier for Proof Key for Code Exchange (PKCE) used in the authorization code grant type. + /// This is used to mitigate authorization code interception attacks. + /// + [BindProperty(SupportsGet = true, Name = Parameters.CodeVerifier)] + public string? CodeVerifier { get; set; } + + /// + /// Maps the properties of this token request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server. + /// + /// A object populated with data from this request. + public Core.TokenRequest Map() + { + return new Core.TokenRequest + { + GrantType = GrantType, + Code = Code, + Password = Password, + Resource = Resource, + Scope = Scope, + RefreshToken = RefreshToken, + RedirectUri = RedirectUri, + CodeVerifier = CodeVerifier, + }; + } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/TokenResponse.cs b/Abblix.Oidc.Server.Mvc/Model/TokenResponse.cs new file mode 100644 index 00000000..fade1ef2 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/TokenResponse.cs @@ -0,0 +1,86 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; +using Abblix.Utils.Json; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents the response from an OAuth 2.0 or OpenID Connect token endpoint, +/// containing details such as access token, token type, and expiration. +/// +public record TokenResponse +{ + private static class Parameters + { + public const string AccessToken = "access_token"; + public const string TokenType = "token_type"; + public const string IssuedTokenType = "issued_token_type"; + public const string ExpiresIn = "expires_in"; + public const string RefreshToken = "refresh_token"; + public const string IdToken = "id_token"; + } + + /// + /// The access token issued by the authorization server. This token is used to access protected resources. + /// + [JsonPropertyName(Parameters.AccessToken)] + [JsonPropertyOrder(1)] + public string AccessToken { get; init; } = default!; + + /// + /// The type of the issued token, typically an absolute URI identifying the token type. + /// + [JsonPropertyName(Parameters.IssuedTokenType)] + [JsonPropertyOrder(2)] + public Uri IssuedTokenType { get; set; } = default!; + + /// + /// The type of token that is issued, usually 'Bearer'. + /// + [JsonPropertyName(Parameters.TokenType)] + [JsonPropertyOrder(3)] + public string TokenType { get; init; } = default!; + + /// + /// The lifetime in seconds of the access token. After this duration, the token will expire and cease to be valid. + /// + [JsonPropertyName(Parameters.ExpiresIn)] + [JsonPropertyOrder(4)] + [JsonConverter(typeof(TimeSpanSecondsConverter))] + public TimeSpan ExpiresIn { get; init; } + + /// + /// The refresh token, which can be used to obtain new access tokens using the same authorization grant. + /// + [JsonPropertyName(Parameters.RefreshToken)] + [JsonPropertyOrder(5)] + public string? RefreshToken { get; init; } + + /// + /// The ID token, which is a JSON Web Token (JWT) that contains the user's identity information. Present in OpenID Connect flows. + /// + [JsonPropertyName(Parameters.IdToken)] + [JsonPropertyOrder(6)] + public string? IdToken { get; init; } +} diff --git a/Abblix.Oidc.Server.Mvc/Model/UserInfoRequest.cs b/Abblix.Oidc.Server.Mvc/Model/UserInfoRequest.cs new file mode 100644 index 00000000..a85fd5f4 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/UserInfoRequest.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Microsoft.AspNetCore.Mvc; +using Core = Abblix.Oidc.Server.Model; +using Parameters = Abblix.Oidc.Server.Model.UserInfoRequest.Parameters; + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents a request for user information, extending from . +/// This record is used primarily in OpenID Connect to retrieve user profile information from the UserInfo endpoint. +/// +public record UserInfoRequest +{ + /// + /// The access token that the client uses to authenticate and authorize the request to the UserInfo endpoint. + /// This token represents the user's consent and grants access to their profile information. + /// + [BindProperty(SupportsGet = true, Name = Parameters.AccessToken)] + public string? AccessToken { get; set; } + + /// + /// Maps the properties of this UserInfo request to a object. + /// This method is used to translate the request data into a format that can be processed by the core logic of the server, + /// enabling the retrieval of user profile information. + /// + /// A object populated with data from this request. + public Core.UserInfoRequest Map() => new() { AccessToken = AccessToken }; +} diff --git a/Abblix.Oidc.Server.Mvc/Model/UserInfoResponse.cs b/Abblix.Oidc.Server.Mvc/Model/UserInfoResponse.cs new file mode 100644 index 00000000..364c9e8c --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Model/UserInfoResponse.cs @@ -0,0 +1,33 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc.Model; + +/// +/// Represents the response containing user information in the context of an OpenID Connect or OAuth 2.0 flow. +/// This record can be extended to include properties that map to the standard claims or custom claims returned +/// by the UserInfo endpoint. +/// +public record UserInfoResponse +{ + //TODO add properties and return the typed response +} diff --git a/Abblix.Oidc.Server.Mvc/MvcUtils.cs b/Abblix.Oidc.Server.Mvc/MvcUtils.cs new file mode 100644 index 00000000..3280f742 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/MvcUtils.cs @@ -0,0 +1,138 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Abblix.Utils; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides utility methods for common MVC operations, including controller and action naming conventions, +/// parameter validation, and model state error handling. +/// +public static class MvcUtils +{ + /// + /// Retrieves the name of a controller type, removing the 'Controller' suffix. + /// + /// The type of the controller. + /// The name of the controller stripped of the 'Controller' suffix. + /// Thrown if the controller's name does not follow the expected + /// naming convention. + /// + /// This method is useful for generating controller names dynamically, such as when building URLs or + /// referencing controllers in logs. + /// + public static string NameOf() where TController : ControllerBase + { + var typeName = typeof(TController).Name; + + const string controllerName = nameof(Controller); + if (!typeName.EndsWith(controllerName)) + { + throw new InvalidOperationException( + $"Naming convention is broken, because the name of the class {typeName} must end with {controllerName}"); + } + + return typeName[..^controllerName.Length]; + } + + /// + /// Removes the 'Async' suffix from the name of an asynchronous action method. + /// + /// The name of the action method. + /// The action name without the 'Async' suffix. + /// Thrown if the action's name does not end with 'Async' as per + /// the naming convention. + /// + /// This method assists in referencing action methods by their logical names without the asynchronous operation + /// indicator. + /// + public static string TrimAsync(string action) + { + const string asyncSuffix = "Async"; + if (!action.EndsWith(asyncSuffix)) + { + throw new InvalidOperationException( + $"Naming convention is broken, because the name of the action {action} must end with {asyncSuffix}"); + } + + return action[..^asyncSuffix.Length]; + } + + private static readonly RequiredAttribute RequiredAttribute = new(); + + /// + /// Validates that an object is not null and satisfies the validation, + /// emulating model validation in ASP.NET MVC. + /// + /// The type of the object being validated. + /// The object to validate. + /// The name of the parameter, used for error message formatting. + /// The validated, non-null object. + /// Thrown if the object is null or does not satisfy + /// the required validation. + /// + /// This method provides a programmatic way to enforce non-nullability and validation of method parameters, + /// mimicking data annotations used in model classes. + /// + public static T Required([NotNull] this T? value, string name) where T : class + { + if (value == null || !RequiredAttribute.IsValid(value)) + { + throw new InvalidOperationException(RequiredAttribute.FormatErrorMessage(name)); + } + + return value; + } + + /// + /// Converts errors contained within a to an . + /// + /// The ModelStateDictionary containing validation errors. + /// An encapsulating the model state errors. + /// + /// This method is useful for aggregating model state errors into a single exception that can be thrown or logged, + /// providing a summary of all validation issues encountered. + /// + private static Exception ToException(this ModelStateDictionary modelState) + { + var errorMessages = + from entry in modelState.Values + from error in entry.Errors + select GetErrorMessage(error); + + return new InvalidOperationException(string.Join(Environment.NewLine, errorMessages)) + { + Data = { { nameof(modelState), modelState } }, + }; + + static string? GetErrorMessage(ModelError error) + { + var errorMessage = error.ErrorMessage; + return errorMessage.HasValue() ? errorMessage : error.Exception?.Message; + } + } +} diff --git a/Abblix.Oidc.Server.Mvc/OidcConst.cs b/Abblix.Oidc.Server.Mvc/OidcConst.cs new file mode 100644 index 00000000..cfce8a1d --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/OidcConst.cs @@ -0,0 +1,35 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Defines constants used throughout the OpenID Connect (OIDC) implementation. +/// +public static class OidcConstants +{ + /// + /// The name of the CORS policy specific to OIDC operations, ensuring that cross-origin requests adhere to security + /// and access configurations. + /// + public const string CorsPolicyName = "OidcCorsPolicy"; +} diff --git a/Abblix.Oidc.Server.Mvc/ParameterValidator.cs b/Abblix.Oidc.Server.Mvc/ParameterValidator.cs new file mode 100644 index 00000000..ba6d0982 --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ParameterValidator.cs @@ -0,0 +1,61 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Abblix.Oidc.Server.Common.Interfaces; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides functionality for validating parameters, emulating the behavior of ASP.NET MVC's +/// . This class implements the interface to offer +/// standard parameter validation routines, enabling its use in core application code where model binding and MVC +/// validation attributes are not directly applicable. +/// +/// +/// Use this class to enforce parameter requirements in service layers, domain models, or other parts of the application +/// where you want to ensure that inputs are not null without relying on MVC's model binding. This is especially useful +/// in scenarios where data validation needs to occur outside of web controllers, ensuring consistency in validation +/// logic across different layers of an application. +/// +public class ParameterValidator : IParameterValidator +{ + /// + /// Validates that a parameter is not null, emulating the enforcement of the in + /// ASP.NET MVC. This method provides a straightforward way to ensure parameters meet their required conditions, + /// throwing a descriptive exception if they are found to be null. + /// + /// The type of the parameter to validate. + /// The parameter value to validate. The method checks if this value is null. + /// The name of the parameter, which is used in constructing the exception message if + /// the parameter is found to be null. This enhances the debuggability by specifying which parameter failed + /// validation. + /// Thrown if the parameter value is null, indicating that a required + /// parameter was not provided. + /// + /// This method simplifies the task of validating mandatory method parameters, ensuring that null values are + /// caught early in the execution flow. By leveraging this method, developers can avoid repetitive null checks + /// and focus on the core logic of their methods. + /// + public void Required([NotNull] T? value, string name) where T : class => value.Required(name); +} diff --git a/Abblix.Oidc.Server.Mvc/Path.cs b/Abblix.Oidc.Server.Mvc/Path.cs new file mode 100644 index 00000000..a2109dcd --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/Path.cs @@ -0,0 +1,97 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Contains constants for various OpenID Connect and OAuth2 endpoint paths. +/// This class centralizes the paths used throughout the application for consistency and maintainability. +/// +public static class Path +{ + private const string Base = "~/connect"; + + /// + /// Path for the authorization endpoint. + /// + public const string Authorize = Base + "/authorize"; + + /// + /// Path for the pushed authorization request (PAR) endpoint. + /// + public const string PushAuthorizationRequest = Base + "/par"; + + /// + /// Path for the user information endpoint. + /// + public const string UserInfo = Base + "/userinfo"; + + /// + /// Path for the end session endpoint. + /// + public const string EndSession = Base + "/endsession"; + + /// + /// Path for the session checking endpoint. + /// + public const string CheckSession = Base + "/checksession"; + + /// + /// Path for the token endpoint. + /// + public const string Token = Base + "/token"; + + /// + /// Path for the token revocation endpoint. + /// + public const string Revocation = Base + "/revoke"; + + /// + /// Path for the token introspection endpoint. + /// + public const string Introspection = Base + "/introspect"; + + /// + /// Path for the backchannel authentication endpoint. + /// + public const string BackchannelAuthentication = Base + "/ciba"; + + /// + /// Path for the device authorization endpoint. + /// + public const string DeviceAuthorization = Base + "/deviceauthorization"; + + /// + /// Path for the client registration endpoint. + /// + public const string Register = Base + "/register"; + + /// + /// Path for the OpenID configuration document. + /// + public const string Configuration = "~/.well-known/openid-configuration"; + + /// + /// Path for the JSON Web Key Set (JWKS) endpoint. + /// + public const string Keys = "~/.well-known/jwks"; +} diff --git a/Abblix.Oidc.Server.Mvc/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server.Mvc/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..5e0e644e --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/ServiceCollectionExtensions.cs @@ -0,0 +1,196 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.DependencyInjection; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Mvc.Binders; +using Abblix.Oidc.Server.Mvc.Configuration; +using Abblix.Oidc.Server.Mvc.Features.SessionManagement; +using Abblix.Oidc.Server.Mvc.Formatters; +using Abblix.Oidc.Server.Mvc.Formatters.Interfaces; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Extension methods for adding OpenID Connect (OIDC) server services to the . +/// These methods simplify the integration of OIDC server capabilities into an ASP.NET Core application, +/// allowing for the configuration of OIDC options and services necessary for authentication and authorization. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds OIDC server services to the specified with provided configuration options. + /// This method allows for configuring OIDC options to tailor the authentication server to your specific needs. + /// + /// The to add services to. + /// A delegate to configure the . + /// The so additional calls can be chained. + + public static IServiceCollection AddOidcServices(this IServiceCollection services, Action configureOptions) + { + return services.AddOidcServices((options, _) => configureOptions(options)); + } + + /// + /// Adds OIDC server services to the specified with provided configuration options and service provider. + /// + /// The to add services to. + /// A delegate to configure the with the service provider. + /// The so additional calls can be chained. + public static IServiceCollection AddOidcServices(this IServiceCollection services, Action configureOptions) + { + return services + .AddOidcCore(configureOptions) + .AddOidcMvc(); + } + + /// + /// Adds OIDC MVC services to the specified . + /// This method sets up MVC services required for handling OIDC endpoints and requests within an ASP.NET Core application. + /// + /// The to add services to. + /// The so additional calls can be chained. + public static IServiceCollection AddOidcMvc( + this IServiceCollection services) + { + services + .AddOidcControllers() + .AddHttpContextAccessor() + + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + .AddScoped() + .Decorate() + + .AddScoped() + .AddScoped() + .AddScoped() + + .AddScoped() + .Decorate() + .AddSingleton() + + .AddScoped() + .AddScoped() + .AddScoped() + + .Configure(options => + { + options.OutputFormatters.Add(new StringOutputFormatter()); + options.ModelBinderProviders.Insert(0, new CultureInfoBinder()); + options.ModelMetadataDetailsProviders.Add(new RequiredBindingMetadataProvider()); + }); + + return services; + } + + /// + /// Adds OIDC controllers to the MVC application. This method is used internally to ensure that the OIDC server's + /// controllers are available to handle authentication and authorization requests. + /// + /// The to add services to. + /// The so additional calls can be chained. + public static IServiceCollection AddOidcControllers(this IServiceCollection services) + { + return services + .AddControllers() + .AddApplicationPart(typeof(ServiceCollectionExtensions).Assembly) + .AddControllersAsServices() + .Services; + } + + /// + /// Configures the application's CORS policy according to the specified . + /// This method enables the customization of CORS policies for OIDC endpoints, allowing for the specification + /// of allowed origins, methods, and headers, as well as whether credentials are supported. + /// + /// The to configure. + /// The name of the CORS policy, used to identify it. + /// The that define the CORS policy. + public static void AppPolicy(this CorsOptions options, string policyName, CorsSettings settings) + { + options.AddPolicy(policyName, policy => + { + settings.AllowedOrigins.ApplyTo(policy.AllowAnyOrigin, policy.WithOrigins); + settings.AllowedMethods.ApplyTo(policy.AllowAnyMethod, policy.WithMethods); + settings.AllowedHeaders.ApplyTo(policy.AllowAnyHeader, policy.WithHeaders); + + if (settings.ExposeHeaders != null) + policy.WithExposedHeaders(settings.ExposeHeaders); + + switch (settings.AllowCredentials) + { + case true: + policy.AllowCredentials(); + break; + + case false: + policy.DisallowCredentials(); + break; + } + + if (settings.MaxAge.HasValue) + policy.SetPreflightMaxAge(settings.MaxAge.Value); + }); + } + + /// + /// Applies configuration based on the specified values to a . + /// This method dynamically configures CORS policies based on the provided values. If a wildcard "*" is provided, + /// it configures the policy to allow any value (origin, method, or header). Otherwise, it applies the specific + /// values provided. This allows for flexible configuration of CORS policies. + /// + /// The array of values to be applied. Can be origins, methods, or headers. + /// A function that configures the policy to allow any values for the setting. + /// A function that applies specific values to the policy. + private static void ApplyTo( + this string[]? values, + Func allowAnyValues, + Func withValues) + { + if (values == null) + return; + + if (values.Length == 1 && values[0] == "*") + allowAnyValues(); + else + withValues(values); + } +} diff --git a/Abblix.Oidc.Server.Mvc/UriResolver.cs b/Abblix.Oidc.Server.Mvc/UriResolver.cs new file mode 100644 index 00000000..e290393b --- /dev/null +++ b/Abblix.Oidc.Server.Mvc/UriResolver.cs @@ -0,0 +1,92 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc.Routing; + +namespace Abblix.Oidc.Server.Mvc; + +/// +/// Provides functionality to generate absolute URIs for MVC actions and content within an ASP.NET Core application. +/// This class utilizes ASP.NET Core's to access the current action context +/// and to create URL helpers for generating URIs. +/// +public class UriResolver : IUriResolver +{ + /// + /// Initializes a new instance of the class with necessary dependencies. + /// + /// The accessor used to obtain the current , + /// providing context for generating URLs, such as the current request's scheme and host. + /// The factory used to create instances of , + /// which facilitates the generation of URLs for actions and content. + public UriResolver( + IActionContextAccessor actionContextAccessor, + IUrlHelperFactory urlHelperFactory) + { + _actionContextAccessor = actionContextAccessor; + _urlHelperFactory = urlHelperFactory; + } + + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IUrlHelperFactory _urlHelperFactory; + + /// + /// Generates an absolute URI for a specified controller action, enabling action methods to be easily referenced + /// across the application for things like redirects or link generation. + /// + /// The name of the action method within the controller. + /// The name of the controller that contains the action method. + /// An object containing the route values for the action method. These values are used to + /// construct the query string and populate parameters in the route template. This parameter is optional. + /// An absolute URI as a object for the specified action within the controller. + /// + /// + /// var uri = uriResolver.Action("Index", "Home", new { id = 42 }); + /// // Result could be something like: "http://example.com/Home/Index?id=42" + /// + /// + public Uri Action(string actionName, string controllerName, object? routeValues = null) + { + var actionContext = _actionContextAccessor.ActionContext.NotNull(nameof(_actionContextAccessor.ActionContext)); + var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext); + + var protocol = actionContext.HttpContext.Request.Scheme; + var action = urlHelper.Action(actionName, controllerName, routeValues, protocol) + ?? throw new ArgumentException($"Can't find action {actionName} in the controller {controllerName}"); + + return new Uri(action, UriKind.Absolute); + } + + /// + public Uri Content(string path) + { + var actionContext = _actionContextAccessor.ActionContext.NotNull(nameof(_actionContextAccessor.ActionContext)); + + var appUrl = actionContext.HttpContext.Request.GetAppUrl(); + return path.StartsWith("~/") + ? new Uri(appUrl + path[1..], UriKind.Absolute) + : new Uri(new Uri(appUrl, UriKind.Absolute), path); + } +} diff --git a/Abblix.Oidc.Server.Tests/Abblix.Oidc.Server.Tests.csproj b/Abblix.Oidc.Server.Tests/Abblix.Oidc.Server.Tests.csproj new file mode 100644 index 00000000..8fd4e6c7 --- /dev/null +++ b/Abblix.Oidc.Server.Tests/Abblix.Oidc.Server.Tests.csproj @@ -0,0 +1,33 @@ + + + + net8.0 + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + PreserveNewest + + + + + + + + + diff --git a/Abblix.Oidc.Server.Tests/AuthenticationTests.cs b/Abblix.Oidc.Server.Tests/AuthenticationTests.cs new file mode 100644 index 00000000..4b94c33d --- /dev/null +++ b/Abblix.Oidc.Server.Tests/AuthenticationTests.cs @@ -0,0 +1,201 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web; +using System.Xml.Linq; +using Xunit; + + +namespace Abblix.Oidc.Server.Tests; + +public sealed class AuthenticationTests: IDisposable +{ + private Config _config; + private CookieContainer _cookies; + private OidcClient _oidcClient; + + public AuthenticationTests() + { + InitializeAsync().Wait(); + } + + private async Task InitializeAsync() + { + _config = new Config(); + + _cookies = new CookieContainer(); + + _oidcClient = await OidcClient.Create( + new HttpClient( + new HttpClientHandler + { + CookieContainer = _cookies, + UseCookies = true, + AllowAutoRedirect = false, + }) + { + BaseAddress = _config.BaseUrl, + }); + } + + public void Dispose() + { + _oidcClient?.Dispose(); + } + + [Fact(Skip = "not ready")] + public async Task BasicFlowTest() + { + var query = new Dictionary(StringComparer.Ordinal) + { + { "client_id", _config.AccountManagementApp.ClientId }, + { "redirect_uri", _config.AccountManagementApp.RedirectUri.AbsoluteUri }, + { "response_type", "id_token" }, + { "scope", "openid profile" }, + { "response_mode", "form_post" }, + { "nonce", Guid.NewGuid().ToString() }, + { "state", Guid.NewGuid().ToString() }, + { "ui_locales", string.Empty }, + }; + + using var tokenResponse = await _oidcClient.Authorize(query); + Assert.Equal(HttpStatusCode.OK, tokenResponse.StatusCode); + + await AssertIdToken("connect/authorize", tokenResponse); + } + + [Fact(Skip = "not ready")] + public async Task RefreshTokenTest() + { + var client = _config.ApClientSampleCode; + + var scope = string.Join(" ", "openid", "profile", "offline_access"); + + // signin/start + //var session = await _oidcClient.SignInStart(_config.AccountManagementApp.ClientId); + + // signin/proceed + //await SignInProceed(session.SessionId, rememberMe: false); + + // connect/authorize + string code; + + var query = new Dictionary(StringComparer.Ordinal) + { + { "client_id", client.ClientId }, + { "redirect_uri", client.RedirectUri.ToString() }, + { "response_type", "code" }, + { "grant_type", "password" }, + { "scope", scope }, + }; + + using (var redirectResponse = await _oidcClient.Authorize(query)) + { + Assert.Equal(HttpStatusCode.Redirect, redirectResponse.StatusCode); + code = HttpUtility.ParseQueryString(redirectResponse.Headers.Location!.Query)["code"]; + Assert.NotNull(code); + } + + string accessToken1; + string refreshToken; + using (var content = new FormUrlEncodedContent(new Dictionary(StringComparer.Ordinal) + { + { "client_id", client.ClientId }, + { "client_secret", client.ClientSecret }, + { "grant_type", "authorization_code" }, + { "scope", scope }, + { "redirect_uri", client.RedirectUri.ToString() }, + { "code", code }, + })) + { + (accessToken1, refreshToken, _) = await _oidcClient.GetToken(content); + } + + await Task.Delay(TimeSpan.FromSeconds(5)); + + // connect/token #2: using refresh_token + string accessToken; + string tokenType; + using (var content = new FormUrlEncodedContent(new Dictionary(StringComparer.Ordinal) + { + { "client_id", client.ClientId }, + { "client_secret", client.ClientSecret }, + { "grant_type", "refresh_token" }, + { "scope", scope }, + { "refresh_token", refreshToken }, + })) + { + (accessToken, _, tokenType) = await _oidcClient.GetToken(content); + } + + Assert.NotEqual(accessToken1, accessToken); + + // connect/userinfo + var userInfo = await _oidcClient.GetUserInfo(tokenType, accessToken); + + Assert.Equal( + new[] { "sub" }, + userInfo.RootElement.EnumerateObject().Select(claim => claim.Name).ToArray() + ); + } + + private async Task AssertIdToken(string expectedPath, HttpResponseMessage response) + { + AssertUri(expectedPath, response.RequestMessage!.RequestUri); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var tokenForm = XDocument.Parse(await response.Content.ReadAsStringAsync()); + var idToken = tokenForm.Descendants("input").Single(e => e.Attribute("name")?.Value == "id_token").Attribute("value")?.Value; + Assert.False(string.IsNullOrEmpty(idToken), "idToken is empty"); + } + + private void AssertUri(string expectedPath, Uri actualUri) + { + var logonPageUriBase = UriBase(actualUri); + Assert.Equal(new Uri(_config.BaseUrl, expectedPath).AbsoluteUri.ToLowerInvariant(), logonPageUriBase.ToLowerInvariant()); + + static string UriBase(Uri uri) => uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped); + } + + [Fact(Skip = "grant_type=password not supported by default")] + public async Task GrantTypePasswordTest() + { + var query = new Dictionary(StringComparer.Ordinal) + { + { "client_id", _config.ApClientSampleCode.ClientId }, + { "client_secret", _config.ApClientSampleCode.ClientSecret }, + { "grant_type", "password" }, + { "scope", string.Join(" ", "profile", "email", "offline_access") }, + { "username", _config.Login }, + { "password", _config.Password }, + }; + + using var content = new FormUrlEncodedContent(query); + await _oidcClient.GetToken(content); + } +} diff --git a/Abblix.Oidc.Server.Tests/AuthorizationContextExtensionsTests.cs b/Abblix.Oidc.Server.Tests/AuthorizationContextExtensionsTests.cs new file mode 100644 index 00000000..4d79da1b --- /dev/null +++ b/Abblix.Oidc.Server.Tests/AuthorizationContextExtensionsTests.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Collections.Generic; +using System.Text.Json.Nodes; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Model; +using Xunit; + +namespace Abblix.Oidc.Server.Tests; + +public class AuthorizationContextExtensionsTests +{ + [Fact] + public void SerializeDeserializeTest() + { + var ac = new AuthorizationContext( + "clientId", + new []{ "scope1", "scope2" }, + new RequestedClaims { UserInfo = new Dictionary + { + { "abc", new RequestedClaimDetails { Essential = true } }, + }}); + + var payload = new JsonWebTokenPayload(new JsonObject()); + ac.ApplyTo(payload); + Assert.Contains(payload.Json, claim => claim.Key == "requested_claims"); + + var ac2 = payload.ToAuthorizationContext(); + Assert.NotNull(ac2.RequestedClaims); + Assert.NotNull(ac2.RequestedClaims.UserInfo); + Assert.True(ac2.RequestedClaims.UserInfo["abc"].Essential); + } +} diff --git a/Abblix.Oidc.Server.Tests/Common.cs b/Abblix.Oidc.Server.Tests/Common.cs new file mode 100644 index 00000000..094a45b4 --- /dev/null +++ b/Abblix.Oidc.Server.Tests/Common.cs @@ -0,0 +1,27 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics.CodeAnalysis; + + + +[assembly: ExcludeFromCodeCoverage] diff --git a/Abblix.Oidc.Server.Tests/Config.cs b/Abblix.Oidc.Server.Tests/Config.cs new file mode 100644 index 00000000..acbcc6df --- /dev/null +++ b/Abblix.Oidc.Server.Tests/Config.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.IO; + +using Microsoft.Extensions.Configuration; + + + +namespace Abblix.Oidc.Server.Tests; + +public sealed class Config +{ + private static IConfiguration GetTestConfiguration(string fileName = "appSettings.json") + { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); + var extension = Path.GetExtension(fileName); + + return new ConfigurationBuilder() + .AddJsonFile(fileName, optional: false, reloadOnChange: false) + .AddJsonFile($"{fileNameWithoutExtension}.{environment}{extension}", optional: true, reloadOnChange: false) + .Build(); + } + + private static readonly IConfiguration AppSettings = GetTestConfiguration(); + + public Config() => AppSettings.Bind(this); + + public ClientSettings AccountManagementApp { get; set; } + public ClientSettings ApClientSampleCode { get; set; } + public Uri BaseUrl { get; set; } + public string Login { get; set; } + public string Password { get; set; } + + public class ClientSettings + { + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public Uri RedirectUri { get; set; } + } +} diff --git a/Abblix.Oidc.Server.Tests/OidcClient.cs b/Abblix.Oidc.Server.Tests/OidcClient.cs new file mode 100644 index 00000000..48d74e79 --- /dev/null +++ b/Abblix.Oidc.Server.Tests/OidcClient.cs @@ -0,0 +1,147 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + + + +namespace Abblix.Oidc.Server.Tests; + +public class OidcClient : IDisposable +{ + public static async Task Create(HttpClient client) + { + var result = new OidcClient(client); + await result.DiscoverEndpoints(); + return result; + } + + private OidcClient(HttpClient client) + { + _client = client; + } + + public void Dispose() + { + _client?.Dispose(); + } + + private async Task DiscoverEndpoints() + { + var discoveryResponse = JsonDocument.Parse(await _client.GetStringAsync(".well-known/openid-configuration")); + + Uri Discover(string name) => new(discoveryResponse.RootElement.GetProperty(name).GetString()); + + _authorizationEndpoint = Discover("authorization_endpoint"); + _tokenEndpoint = Discover("token_endpoint"); + _userInfoEndpoint = Discover("userinfo_endpoint"); + } + + public async Task Authorize(IDictionary parameters) + { + using var content = new FormUrlEncodedContent(parameters); + + return await _client.PostAsync(_authorizationEndpoint, content); + } + + // connect/token + public async Task<(string accessToken, string refreshToken, string tokenType)> GetToken(HttpContent request) + { + using var tokenResponse = await _client.PostAsync(_tokenEndpoint, request); + + //await AssertExpectedStatusCode(tokenResponse, HttpStatusCode.OK); + //await AssertExpectedContentMediaType(tokenResponse, "application/json"); + + JsonDocument response; + using (var tokenContent = tokenResponse.Content) + { + await using var stream = await tokenContent.ReadAsStreamAsync(); + response = await JsonDocument.ParseAsync(stream); + } + + string GetString(string name) + => response.RootElement.TryGetProperty(name, out var value) + ? value.GetString() + : throw new InvalidOperationException($"{name} is null"); + + var accessToken = GetString("access_token"); + var refreshToken = GetString("refresh_token"); + var tokenType = GetString("token_type"); + + var tokenParts = accessToken.Split('.'); + if (tokenParts.Length != 3) + { + throw new InvalidOperationException($"token must contain 3 part but contains {tokenParts.Length}"); + } + + var payload = JsonDocument.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(tokenParts[1]))); + + var expected = new[] { "nbf", "exp", "iss", "client_id", "sub", "auth_time", "idp", "scope", "sid", "jti", "iat" }; + var actual = (from claim in payload.RootElement.EnumerateObject() select claim.Name).ToArray(); + + var missingClaims = expected.Except(actual).ToArray(); + if (0 < missingClaims.Length) + { + throw new InvalidOperationException($"The following claims are missing: {string.Join(", ", missingClaims)}"); + } + + var unexpectedClaims = actual.Except(expected).ToArray(); + if (0 < unexpectedClaims.Length) + { + throw new InvalidOperationException($"The following claims are unexpected: {string.Join(", ", unexpectedClaims)}"); + } + + return (accessToken, refreshToken, tokenType); + } + + // connect/userinfo + public async Task GetUserInfo(string tokenType, string accessToken) + { + var authenticationHeaderValue = new AuthenticationHeaderValue(tokenType, accessToken); + + using var request = new HttpRequestMessage(HttpMethod.Get, _userInfoEndpoint) + { + Headers = { Authorization = authenticationHeaderValue }, + }; + + using var userInfoResponse = await _client.SendAsync(request); + + //await AssertExpectedStatusCode(userInfoResponse, HttpStatusCode.OK); + //await AssertExpectedContentMediaType(userInfoResponse, MediaTypeNames.Application.Json); + + await using var stream = await userInfoResponse.Content.ReadAsStreamAsync(); + return await JsonDocument.ParseAsync(stream); + } + + private readonly HttpClient _client; + + private Uri _authorizationEndpoint; + private Uri _tokenEndpoint; + private Uri _userInfoEndpoint; +} diff --git a/Abblix.Oidc.Server.Tests/appsettings.json b/Abblix.Oidc.Server.Tests/appsettings.json new file mode 100644 index 00000000..1ebe519e --- /dev/null +++ b/Abblix.Oidc.Server.Tests/appsettings.json @@ -0,0 +1,29 @@ +{ + "BaseUrl": "https://localhost/AuthenticationFacadeApp/", + "AccountManagementApp": { + "ClientId": "AccountManagementApp", + "ClientSecret": "secret", + "RedirectUri": "https://localhost/AccountManagementApp/signin-oidc" + }, + "ApClientSampleCode": { + "ClientId": "ApClientSampleCode", + "ClientSecret": "26f8bc29-ef95-4f26-bc5c-2bb8a392c700", + "RedirectUri": "https://localhost/ApClientSample" + }, + + "Realm": "https://localhost/UisClientSample/", + "Login": "test@test.com", + "Password": "1", + + "DataProtection": { + "encrypt": { + "algorithm": "AES", + "key": "xYWCa3lxI7V4nZTqSZnsLy+yu9i3YaUeMHEFLbt6iIk=", + "iv": "yyGJRuLtqpqM1Sz+7sy3qw==" + }, + "sign": { + "algorithm": "HMACSHA1", + "key": "k7YMosY0FhblZFDMtsuobQ==" + } + } +} \ No newline at end of file diff --git a/Abblix.Oidc.Server.UnitTests/Abblix.Oidc.Server.UnitTests.csproj b/Abblix.Oidc.Server.UnitTests/Abblix.Oidc.Server.UnitTests.csproj new file mode 100644 index 00000000..c632d31a --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Abblix.Oidc.Server.UnitTests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Abblix.Oidc.Server.UnitTests/ClientAuthentication/AuthorizationHeaderAdapterTests.cs b/Abblix.Oidc.Server.UnitTests/ClientAuthentication/AuthorizationHeaderAdapterTests.cs new file mode 100644 index 00000000..4f711670 --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/ClientAuthentication/AuthorizationHeaderAdapterTests.cs @@ -0,0 +1,69 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; +using HashAlgorithm = Abblix.Oidc.Server.Features.Hashing.HashAlgorithm; + +namespace Abblix.Oidc.Server.UnitTests.ClientAuthentication; + +public class AuthorizationHeaderAdapterTests +{ + [Theory] + [InlineData("id", "secret")] + [InlineData("id", "secret:2")] + public async Task BasicAuthorizationHeaderTest(string id, string secret) + { + var hashService = new Mock(MockBehavior.Strict); + var secretHashCode = Encoding.ASCII.GetBytes(secret); + hashService.Setup(s => s.Sha(HashAlgorithm.Sha512, secret)).Returns(secretHashCode); + + var clientInfo = new ClientInfo(id) + { + ClientSecrets = [new ClientSecret { Sha512Hash = secretHashCode }], + }; + + var clientInfoProvider = new Mock(MockBehavior.Strict); + clientInfoProvider.Setup(p => p.TryFindClientAsync(id)).ReturnsAsync(clientInfo); + + var parameter = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{id}:{secret}")); + var request = new ClientRequest { AuthorizationHeader = new AuthenticationHeaderValue("Basic", parameter) }; + + var adapter = new ClientSecretBasicAuthenticator( + new Mock>().Object, + clientInfoProvider.Object, + TimeProvider.System, + hashService.Object); + + var result = await adapter.TryAuthenticateClientAsync(request); + Assert.Equal(clientInfo, result); + } +} diff --git a/Abblix.Oidc.Server.UnitTests/Common.cs b/Abblix.Oidc.Server.UnitTests/Common.cs new file mode 100644 index 00000000..094a45b4 --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Common.cs @@ -0,0 +1,27 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics.CodeAnalysis; + + + +[assembly: ExcludeFromCodeCoverage] diff --git a/Abblix.Oidc.Server.UnitTests/Controllers/DiscoveryUnitTests.cs b/Abblix.Oidc.Server.UnitTests/Controllers/DiscoveryUnitTests.cs new file mode 100644 index 00000000..f675dfcc --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Controllers/DiscoveryUnitTests.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.UnitTests.Controllers; + +public class DiscoveryUnitTests +{ + //[DataTestMethod] + //[TestCase(ResponseTypes.None, "\"none\"")] + //[TestCase(ResponseTypes.IdToken, "\"id_token\"")] + //[TestCase(ResponseTypes.IdToken | ResponseTypes.Token, "\"token id_token\"")] + //public void FlagsEnumStringConverterTest(string[] source, string expected) + //{ + // var options = new JsonSerializerOptions { Converters = { new EnumStringConverter() } }; + // var json = JsonSerializer.Serialize(source, options); + // Assert.AreEqual(expected, json); + + // var deserialized = JsonSerializer.Deserialize(json, options); + // Assert.AreEqual(source, deserialized); + //} + + //[DataTestMethod] + //[TestCase(GrantType.AuthorizationCode, "\"authorization_code\"")] + //[TestCase(GrantType.ClientCredentials, "\"client_credentials\"")] + //[TestCase(GrantType.RefreshToken, "\"refresh_token\"")] + //[TestCase(GrantType.Implicit, "\"implicit\"")] + //[TestCase(GrantType.Password, "\"password\"")] + //public void EnumStringConverterTest(GrantType source, string expected) + //{ + // var options = new JsonSerializerOptions { Converters = { new EnumStringConverter() } }; + // var json = JsonSerializer.Serialize(source, options); + // Assert.AreEqual(expected, json); + + // var deserialized = JsonSerializer.Deserialize(json, options); + // Assert.AreEqual(source, deserialized); + //} +} diff --git a/Abblix.Oidc.Server.UnitTests/Endpoints/Token/AuthorizationCodeGrantHandlerTests.cs b/Abblix.Oidc.Server.UnitTests/Endpoints/Token/AuthorizationCodeGrantHandlerTests.cs new file mode 100644 index 00000000..87b63ecb --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Endpoints/Token/AuthorizationCodeGrantHandlerTests.cs @@ -0,0 +1,108 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.Threading.Tasks; + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Grants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; + +using Moq; + +using Xunit; + + +namespace Abblix.Oidc.Server.UnitTests.Endpoints.Token; + +public class AuthorizationCodeGrantHandlerTests +{ + private readonly Mock _authCodeService; + private readonly Mock _parameterValidator; + private readonly AuthorizationCodeGrantHandler _handler; + + public AuthorizationCodeGrantHandlerTests() + { + _authCodeService = new Mock(MockBehavior.Strict); + _parameterValidator = new Mock(MockBehavior.Strict); + + _handler = new AuthorizationCodeGrantHandler( + _parameterValidator.Object, + _authCodeService.Object); + } + + [Theory] + [InlineData(CodeChallengeMethods.S256, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")] + [InlineData(CodeChallengeMethods.Plain, "qwerty", "qwerty")] + public async Task PkceSuccessfulChallengeTest(string codeChallengeMethod, string codeChallenge, string codeVerifier) + { + var result = await PkceTest(codeChallengeMethod, codeChallenge, codeVerifier); + + // assert + Assert.IsType(result); + } + + [Theory] + [InlineData(CodeChallengeMethods.S256, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "abc")] + [InlineData(CodeChallengeMethods.S256, "qwerty", null)] + [InlineData(CodeChallengeMethods.Plain, "qwerty", "asdfgh")] + [InlineData(CodeChallengeMethods.Plain, "qwerty", null)] + public async Task PkceFailureChallengeTest(string codeChallengeMethod, string codeChallenge, string codeVerifier) + { + var result = await PkceTest(codeChallengeMethod, codeChallenge, codeVerifier); + + // assert + Assert.IsType(result); + var invalidGrantResult = (InvalidGrantResult)result; + + Assert.Equal(ErrorCodes.InvalidGrant, invalidGrantResult.Error); + } + + private async Task PkceTest(string codeChallengeMethod, string codeChallenge, string codeVerifier) + { + // arrange + var clientInfo = new ClientInfo("client1"); + var tokenRequest = new TokenRequest { Code = "abc", CodeVerifier = codeVerifier }; + _parameterValidator.Setup(v => v.Required(tokenRequest.Code, "Code")); + + _authCodeService + .Setup(s => s.AuthorizeByCodeAsync(tokenRequest.Code)) + .ReturnsAsync( + new AuthorizedGrant( + new AuthSession("123", "session1", DateTimeOffset.UtcNow, "ip"), + Context: new AuthorizationContext(clientInfo.ClientId, [Scopes.OpenId], null) + { + CodeChallenge = codeChallenge, + CodeChallengeMethod = codeChallengeMethod, + })); + + // act + var result = await _handler.AuthorizeAsync(tokenRequest, clientInfo); + return result; + } +} diff --git a/Abblix.Oidc.Server.UnitTests/Features/Licensing/LicenseManagerTests.cs b/Abblix.Oidc.Server.UnitTests/Features/Licensing/LicenseManagerTests.cs new file mode 100644 index 00000000..f6070bf1 --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Features/Licensing/LicenseManagerTests.cs @@ -0,0 +1,133 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System; +using System.Linq; +using Abblix.Oidc.Server.Features.Licensing; +using Xunit; + +namespace Abblix.Oidc.Server.UnitTests.Features.Licensing; + +public class LicenseManagerTests +{ + private static License CreateLicense(int? notBefore, int? expiresAt, int? gracePeriod = null) + { + var utcNow = DateTimeOffset.UtcNow; + return new License + { + NotBefore = notBefore.HasValue ? utcNow.AddDays(notBefore.Value) : null, + ExpiresAt = expiresAt.HasValue ? utcNow.AddDays(expiresAt.Value) : null, + GracePeriod = gracePeriod.HasValue ? utcNow.AddDays(gracePeriod.Value) : null, + }; + } + + /// + /// Tests that licenses are inserted in the correct order based on their validity period + /// using the binary search method. It verifies that the LicenseManager maintains the + /// licenses list in a sorted state, facilitating efficient license status evaluation. + /// + [Fact] + public void AddLicense_ShouldInsertLicensesInSortedOrder() + { + var manager = new LicenseManager(); + var license1 = CreateLicense(-1, 1); // License valid from yesterday to tomorrow + var license2 = CreateLicense(-3, -1); // License that expired yesterday + var license3 = CreateLicense(1, 3); // License that starts tomorrow + + manager.AddLicense(license1); + manager.AddLicense(license2); + manager.AddLicense(license3); + + var licenses = manager.GetLicenses().ToList(); + + // Verify the order is maintained as expected: license2, license1, license3 + Assert.Equal(new[] { license2, license1, license3 }, licenses); + } + + /// + /// Tests that GenerateActiveLicense correctly evaluates and returns the most appropriate + /// active license based on the current time, handling various license states including + /// active, expired, and within grace periods. It ensures that the logic for prioritizing + /// licenses and updating the current license index is accurately implemented. + /// + [Fact] + public void GenerateActiveLicense_ShouldReturnCorrectActiveLicense() + { + var manager = new LicenseManager(); + var activeLicense = CreateLicense(-1, 10); // Currently active license + var expiredLicense = CreateLicense(-20, -10); // Expired license + var futureLicense = CreateLicense(1, 20); // License that will be active in the future + + manager.AddLicense(futureLicense); + manager.AddLicense(expiredLicense); + manager.AddLicense(activeLicense); + + var result = manager.GenerateActiveLicense(DateTimeOffset.UtcNow); + + Assert.NotNull(result); + Assert.Equal(activeLicense, result); + } + + /// + /// Tests that GenerateActiveLicense correctly identifies and returns a license that is + /// within its grace period if no other active licenses are found. It also checks that + /// licenses in their grace period are only considered if no active licenses are available, + /// adhering to the prioritization of license states. + /// + [Fact] + public void GenerateActiveLicense_ShouldCorrectlyHandleGracePeriodLicenses() + { + var manager = new LicenseManager(); + var gracePeriodLicense = CreateLicense(-10, -5, 5); // License in grace period + var activeLicense = CreateLicense(-1, 10); // Active license + + manager.AddLicense(gracePeriodLicense); + manager.AddLicense(activeLicense); + + var result = manager.GenerateActiveLicense(DateTimeOffset.UtcNow); + + // Active license should take precedence over grace period license + Assert.NotNull(result); + Assert.Equal(activeLicense, result); + } + + /// + /// Tests that GenerateActiveLicense correctly logs warnings for licenses that are + /// nearing expiration within a month. It ensures that the logging mechanism is + /// triggered appropriately for licenses close to their expiration date. + /// + [Fact] + public void GenerateActiveLicense_ShouldLogWarningForExpiringLicenses() + { + var manager = new LicenseManager(); + var nearExpiryLicense = CreateLicense(-1, 30); // License expiring in a month + + manager.AddLicense(nearExpiryLicense); + + var result = manager.GenerateActiveLicense(DateTimeOffset.UtcNow); + + // This test assumes the existence of a mechanism to verify log entries + // Example assertion, depending on the logging framework used + Assert.NotNull(result); + // Verify log contains a warning about the nearing expiration of the license + } +} diff --git a/Abblix.Oidc.Server.UnitTests/Model/ClientRegistrationRequestTest.cs b/Abblix.Oidc.Server.UnitTests/Model/ClientRegistrationRequestTest.cs new file mode 100644 index 00000000..f99f9012 --- /dev/null +++ b/Abblix.Oidc.Server.UnitTests/Model/ClientRegistrationRequestTest.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using Abblix.Oidc.Server.Model; +using Xunit; + +namespace Abblix.Oidc.Server.UnitTests.Model; + +public class ClientRegistrationRequestTest +{ + [Theory] + [InlineData( + "{\"client_name\":\"dynamic_client_1 RqxLk9BdhK8qC3z\",\"grant_types\":[\"implicit\"],\"jwks\":{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"gUOdYo2PpUUnZzozIPJ-7mK2Z5jYBxjj_5iB2TDnElt8yUc-mcCeOQrsaswPgKx2KMSJ50kwrFHHEuNyiDhgNMgtmJ98RuhggXaPF1fmmHss_Wc1OSqyGYLWbEzYGsRck5yTVP4xsPYAeP5xkkLze_FXJvwITNu2aGxXEYwokkrcWgL3AsXtYKClIwmacHhVNEMn-ALe3sMTifx4F8TqmNAlD4FPga094txHJNo2Ho6z4kn5L4uq_WXklDjaIDOqQZtdn0emXig3RHQcOtepFcXt7pcK9E2M3kxKFOMPpY8c4kaDfQ41jv23vbm9oDTh5s3TB0ZwcKJXj4-06gwTWw\"}]},\"token_endpoint_auth_method\":\"client_secret_basic\",\"response_types\":[\"id_token token\"],\"redirect_uris\":[\"https://www.certification.openid.net/test/a/Abblix/callback\"],\"contacts\":[\"certification@oidf.org\"]}")] + public void DeserializeClientRegistrationRequestTest(string json) + { + var request = JsonSerializer.Deserialize(json); + } +} diff --git a/Abblix.Oidc.Server/Abblix.Oidc.Server.csproj b/Abblix.Oidc.Server/Abblix.Oidc.Server.csproj new file mode 100644 index 00000000..cf29dcef --- /dev/null +++ b/Abblix.Oidc.Server/Abblix.Oidc.Server.csproj @@ -0,0 +1,44 @@ + + + + enable + true + Abblix.OIDC.Server + Abblix OIDC Server + Flexible OpenID Connect and OAuth 2.0 server-side implementation for modern ASP.NET projects + net6.0;net7.0;net8.0 + true + Abblix LLP + https://www.abblix.com/abblix-oidc-server + https://github.com/Abblix/Oidc.Server + git + Abblix OIDC OpenID OpenID-Connect Authentication Authorization Security Identity OAuth OAuth2 SSO Single-Sign-On ASP.NET IdentityServer Federation Claims WebApi + README.md + LICENSE.md + Copyright (c) 2024 Abblix LLP. All rights reserved. + For detailed release notes, visit: https://github.com/Abblix/Oidc.Server/releases + Abblix.png + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Abblix.Oidc.Server/Common/AuthorizationContext.cs b/Abblix.Oidc.Server/Common/AuthorizationContext.cs new file mode 100644 index 00000000..b32962b2 --- /dev/null +++ b/Abblix.Oidc.Server/Common/AuthorizationContext.cs @@ -0,0 +1,86 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Common; + +/// +/// Represents the context of an authorization process, encapsulating key parameters necessary for processing +/// authorization requests. +/// +/// +/// This record is pivotal for tracking the state of an authorization request throughout its lifecycle. +/// It encapsulates details that are critical for the secure issuance of authorization codes and tokens, +/// while ensuring compliance with OAuth 2.0 and OpenID Connect protocols. The context facilitates +/// not just the validation of requests at the token endpoint, but also supports secure interactions +/// by incorporating mechanisms like PKCE and nonce values to mitigate common attack vectors such as +/// code injection and replay attacks. Furthermore, it carries information about requested scopes and claims, +/// enabling fine-grained access control and personalized identity assertion in accordance with the client's needs +/// and the authorization server's policies. +/// +public record AuthorizationContext(string ClientId, string[] Scope, RequestedClaims? RequestedClaims) +{ + /// + /// The unique identifier for the client making the authorization request, as registered in the authorization server. + /// This identifier is crucial for linking the authorization request and the issued tokens to a specific client application. + /// + public string ClientId { get; init; } = ClientId; + + /// + /// Defines the scope of access requested by the client. Scopes are used to specify the level of access or permissions + /// that the client is requesting on the user's behalf. They play a key role in enforcing principle of least privilege. + /// + public string[] Scope { get; init; } = Scope; + + /// + /// Optional. Specifies the individual Claims requested by the client, providing detailed instructions + /// for the authorization server on the Claims to be returned, either in the ID Token or via the UserInfo endpoint. + /// This mechanism supports clients in obtaining consented user information in a structured and controlled manner. + /// + public RequestedClaims? RequestedClaims { get; init; } = RequestedClaims; + + /// + /// The URI where the authorization response should be sent. This URI must match one of the registered redirect URIs + /// for the client application, ensuring that authorization responses are delivered to the correct destination securely. + /// + public Uri? RedirectUri { get; init; } + + /// + /// A string value used to associate a client session with an ID Token, mitigating replay attacks by ensuring + /// that an ID Token cannot be used in a different context than the one it was intended for. + /// + public string? Nonce { get; init; } + + /// + /// The high-entropy cryptographic string provided by the client, used in the PKCE (Proof Key for Code Exchange) + /// extension to secure the exchange of the authorization code for a token, + /// especially in public clients and mobile applications. + /// + public string? CodeChallenge { get; init; } + + /// + /// Specifies the transformation method applied to the 'code_verifier' when generating the 'code_challenge', + /// enhancing the security of PKCE by allowing the authorization server to verify the code exchange authenticity. + /// + public string? CodeChallengeMethod { get; init; } +} diff --git a/Abblix.Oidc.Server/Common/AuthorizationContextExtensions.cs b/Abblix.Oidc.Server/Common/AuthorizationContextExtensions.cs new file mode 100644 index 00000000..c6444901 --- /dev/null +++ b/Abblix.Oidc.Server/Common/AuthorizationContextExtensions.cs @@ -0,0 +1,78 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; +using Abblix.Jwt; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Common; + +/// +/// Provides extension methods for working with objects, +/// facilitating the conversion between authorization contexts and JWT claims. +/// +public static class AuthorizationContextExtensions +{ + private static readonly JsonSerializerOptions JsonSerializerOptions = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + + /// + /// Applies the information from an to a , + /// converting the context into JWT claims. + /// + /// The containing authorization details. + /// The JWT payload where the authorization context information will be applied as claims. + /// + /// This method is useful for embedding authorization details directly into a JWT, allowing for efficient transfer + /// and validation of authorization information. + /// + public static void ApplyTo(this AuthorizationContext context, JsonWebTokenPayload payload) + { + payload.ClientId = context.ClientId; + payload.Scope = context.Scope; + payload.Nonce = context.Nonce; + payload[JwtClaimTypes.RequestedClaims] = JsonSerializer.SerializeToNode(context.RequestedClaims, JsonSerializerOptions); + } + + /// + /// Creates an from a , + /// converting JWT claims back into an authorization context. + /// + /// The JWT payload containing claims that represent an authorization context. + /// An instance of populated with information derived from + /// the JWT claims. + /// + /// This method facilitates the extraction of authorization details from JWT claims, + /// reconstructing an for further processing or validation. + /// + public static AuthorizationContext ToAuthorizationContext(this JsonWebTokenPayload payload) + { + return new AuthorizationContext( + payload.ClientId.NotNull(nameof(payload.ClientId)), + payload.Scope.NotNull(nameof(payload.Scope)).ToArray(), + payload[JwtClaimTypes.RequestedClaims].Deserialize(JsonSerializerOptions)); + } +} diff --git a/Abblix.Oidc.Server/Common/ClaimsExtensions.cs b/Abblix.Oidc.Server/Common/ClaimsExtensions.cs new file mode 100644 index 00000000..7af67304 --- /dev/null +++ b/Abblix.Oidc.Server/Common/ClaimsExtensions.cs @@ -0,0 +1,103 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Claims; + +using Abblix.Jwt; + + + +namespace Abblix.Oidc.Server.Common; + +/// +/// Several handy extensions for principal and claims. +/// +public static class ClaimsExtensions +{ + public static bool IsAuthenticated(this ClaimsPrincipal principal) + => principal is { Identity.IsAuthenticated: true }; + + /// + /// These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. + /// Some of them are: + /// iss (issuer), + /// exp (expiration time), + /// sub (subject), + /// aud (audience), + /// and others. + /// + public static IEnumerable GetRegisteredClaims(this IEnumerable claims) => claims.Where(IsRegistered); + + private static bool IsRegistered(this Claim claim) => IanaClaimTypes.Registered.Contains(claim.Type); + + /// + /// These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry + /// or be defined as a URI that contains a collision resistant namespace. + /// + public static IEnumerable GetPublicClaims(this IEnumerable claims) => claims.Where(IsPublic); + + private static bool IsPublic(this Claim claim) => IanaClaimTypes.Public.Contains(claim.Type); + + /// + /// These are the custom claims created to share information between parties that agree on using them and are neither registered or public claims. + /// + public static IEnumerable GetPrivateClaims(this IEnumerable claims) + => claims.Where(claim => !claim.IsRegistered() && !claim.IsPublic()); + + public static IEnumerable ExcludeClaims(this IEnumerable claims, params string[] claimTypes) + => claims.ExceptBy(claimTypes, claim => claim.Type, StringComparer.OrdinalIgnoreCase); + + public static IEnumerable ExcludeRegisteredClaims(this IEnumerable claims) + => claims.Where(claim => !claim.IsRegistered()); + + public static Claim FindRequired(this IEnumerable claims, string name) + => claims.Find(name) ?? throw new InvalidOperationException($"The claim {name} is not found"); + + public static Claim? Find(this IEnumerable claims, string name) + => claims.FirstOrDefault(claim => string.Equals(claim.Type, name, StringComparison.OrdinalIgnoreCase)); + + public static string? FindValue(this IEnumerable claims, string name) + => claims.Find(name)?.Value; + + public static void SetClaim(this ICollection claims, string name, string? value) + { + var claim = claims.FirstOrDefault(c => c.Type == name); + if (claim != null) + { + if (claim.Value == value) + { + return; + } + + claims.Remove(claim); + } + + if (value == null) + return; + + claim = new Claim(name, value); + claims.Add(claim); + } + + public static IEnumerable GetUserClaimsOnly(this IEnumerable claims) + => claims.Where(claim => claim.Type == JwtClaimTypes.Subject || !IanaClaimTypes.Registered.Contains(claim.Type)); +} diff --git a/Abblix.Oidc.Server/Common/Configuration/BackChannelLogoutOptions.cs b/Abblix.Oidc.Server/Common/Configuration/BackChannelLogoutOptions.cs new file mode 100644 index 00000000..eb2204cd --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/BackChannelLogoutOptions.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Represents options for back-channel logout for a client. +/// +public record BackChannelLogoutOptions(Uri Uri, bool RequiresSessionId = true) +{ + /// + /// Gets or initializes the logout URI. + /// + public Uri Uri { get; init; } = Uri; + + /// + /// Gets or initializes whether a session ID is for logout. + /// + public bool RequiresSessionId { get; init; } = RequiresSessionId; + + /// + /// Gets or sets the duration a logout token is valid for. + /// + public TimeSpan LogoutTokenExpiresIn { get; set; } = TimeSpan.FromMinutes(5); +} diff --git a/Abblix.Oidc.Server/Common/Configuration/CheckSessionCookieOptions.cs b/Abblix.Oidc.Server/Common/Configuration/CheckSessionCookieOptions.cs new file mode 100644 index 00000000..ad0f8d79 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/CheckSessionCookieOptions.cs @@ -0,0 +1,48 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Defines options for the session check cookie used in monitoring changes in session status. +/// +public record CheckSessionCookieOptions +{ + /// + /// The name of the cookie used to monitor session status changes. The default value is "Abblix.SessionId". + /// + public string Name { get; init; } = "Abblix.SessionId"; + + /// + /// The domain name where the cookie is available. Specifying the domain restricts where the cookie is sent. + /// Leaving this value null means the cookie is sent to all subdomains. + /// + public string? Domain { get; set; } + + /// + /// The SameSite attribute for the cookie which asserts that a cookie must not be sent with cross-origin requests, + /// providing some protection against cross-site request forgery attacks (CSRF). + /// The default value is "None", which permits the cookie to be sent with cross-site requests. + /// Valid options are "None", "Lax", and "Strict". + /// + public string SameSite { get; set; } = "None"; +} diff --git a/Abblix.Oidc.Server/Common/Configuration/ClientIdOptions.cs b/Abblix.Oidc.Server/Common/Configuration/ClientIdOptions.cs new file mode 100644 index 00000000..a7f73792 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/ClientIdOptions.cs @@ -0,0 +1,34 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Represents options for generating client IDs. +/// +public record ClientIdOptions +{ + /// + /// Gets or sets the length of the generated client ID. + /// + public int Length { get; init; } = 16; +} diff --git a/Abblix.Oidc.Server/Common/Configuration/ClientSecretOptions.cs b/Abblix.Oidc.Server/Common/Configuration/ClientSecretOptions.cs new file mode 100644 index 00000000..3169fc15 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/ClientSecretOptions.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Configuration options for generating client secrets in OAuth2/OpenID Connect authentication. +/// +public record ClientSecretOptions +{ + /// + /// The length of the generated client secret. + /// Specifies the number of characters in the secret. Default value is 16. + /// + public int Length { get; init; } = 16; + + /// + /// The expiration duration for the client secret. + /// Defines how long after creation the secret will remain valid. + /// Default value is 30 days. + /// + public TimeSpan ExpiresAfter { get; init; } = TimeSpan.FromDays(30); +} diff --git a/Abblix.Oidc.Server/Common/Configuration/DiscoveryOptions.cs b/Abblix.Oidc.Server/Common/Configuration/DiscoveryOptions.cs new file mode 100644 index 00000000..95377053 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/DiscoveryOptions.cs @@ -0,0 +1,34 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Defines discovery endpoint options. +/// +public class DiscoveryOptions +{ + /// + /// Allows to expose exact paths to OIDC endpoints via discovery manifest. + /// + public bool AllowEndpointPathsDiscovery { get; set; } = true; +} diff --git a/Abblix.Oidc.Server/Common/Configuration/FrontChannelLogoutOptions.cs b/Abblix.Oidc.Server/Common/Configuration/FrontChannelLogoutOptions.cs new file mode 100644 index 00000000..92d0d362 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/FrontChannelLogoutOptions.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Represents options for front-channel logout for a client. +/// +public record FrontChannelLogoutOptions(Uri Uri, bool RequiresSessionId = true); diff --git a/Abblix.Oidc.Server/Common/Configuration/NewClientOptions.cs b/Abblix.Oidc.Server/Common/Configuration/NewClientOptions.cs new file mode 100644 index 00000000..ccce4333 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/NewClientOptions.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Represents options for creating a new client. +/// +public record NewClientOptions +{ + /// + /// The options for generating a client ID. + /// + public ClientIdOptions ClientId { get; init; } = new(); + + /// + /// The options for generating a client secret. + /// + public ClientSecretOptions ClientSecret { get; init; } = new(); +} diff --git a/Abblix.Oidc.Server/Common/Configuration/OidcEndpoints.cs b/Abblix.Oidc.Server/Common/Configuration/OidcEndpoints.cs new file mode 100644 index 00000000..638b1b7d --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/OidcEndpoints.cs @@ -0,0 +1,88 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Flags to represent various OIDC endpoints. +/// +[Flags] +public enum OidcEndpoints +{ + /// + /// All OIDC endpoints available. + /// + All = Configuration | Keys | Authorize | Token | UserInfo | CheckSession | EndSession | Revocation | Register | + PushedAuthorizationRequest, + + /// + /// Provides OpenID Connect configuration details. Typically used during client setup. + /// + Configuration = 1 << 0, + + /// + /// Provides public keys for token validation. Essential for token validation. + /// + Keys = 1 << 1, + + /// + /// Used to initiate the authorization process. The starting point for user authentication. + /// + Authorize = 1 << 2, + + /// + /// Used to exchange authorization codes for tokens. Part of the authentication flow. + /// + Token = 1 << 3, + + /// + /// Retrieves user information after authentication. Often used to fetch user details. + /// + UserInfo = 1 << 4, + + /// + /// Used for session monitoring in single sign-on scenarios. Helps track user sessions. + /// + CheckSession = 1 << 5, + + /// + /// Used to end the user's session. Enables logging out the user. + /// + EndSession = 1 << 6, + + /// + /// Used to revoke tokens, enhancing security by invalidating tokens. + /// + Revocation = 1 << 7, + + /// + /// Used to introspect tokens. + /// + Introspection = 1 << 7, + + /// + /// Used to dynamically register clients. Allows clients to register with the OIDC provider. + /// + Register = 1 << 8, + + PushedAuthorizationRequest = 1 << 9, +} diff --git a/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs b/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs new file mode 100644 index 00000000..f26b13a2 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs @@ -0,0 +1,157 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// The root of the OIDC configuration. Provides the simplest way to configure and start your OIDC server. +/// +public record OidcOptions +{ + /// + /// Configuration options for OIDC discovery. These options control how the OIDC server advertises its capabilities + /// and endpoints to clients through the OIDC Discovery mechanism. Proper configuration ensures that clients can + /// dynamically discover information about the OIDC server, such as URLs for authorization, token, userinfo, and + /// JWKS endpoints, supported scopes, response types, and more. + /// + public DiscoveryOptions Discovery { get; set; } = new(); + + /// + /// Represents the unique identifier of the OIDC server. + /// It is recommended to use a URL that is controlled by the entity operating the OIDC server, and it should be + /// consistent across different environments to maintain trust with client applications. + /// + public string? Issuer { get; set; } + + /// + /// A collection of client configurations supported by this OIDC server. Each object + /// defines the settings and capabilities of a registered client, including client ID, client secrets, + /// redirect URIs, and other OAuth2/OIDC parameters. Proper client configuration is essential for securing client + /// applications and enabling them to interact with the OIDC server according to the OAuth2 and OIDC specifications. + /// + public IEnumerable Clients { get; set; } = Array.Empty(); + + /// + /// The URL to a user interface or service that allows users to select an account during the authentication process. + /// This is useful in scenarios where users have multiple accounts and need to choose which one to use for signing in. + /// + public Uri? AccountSelectionUri { get; set; } + + /// + /// The URL to a user interface or service for obtaining user consent during the authentication process. Consent is + /// often required when the client application requests access to user data or when sharing information between + /// different parties. This URI should point to a page or API that can manage consent workflows and communicate + /// the user's decisions back to the OIDC server. + /// + public Uri? ConsentUri { get; set; } + + /// + /// The URL to a user interface or service for handling additional interactions required during the authentication + /// process. This can include multiple factor authentication, user consent, or any custom interaction required by + /// the authentication flow. The OIDC server can redirect users to this URI when additional interaction is needed. + /// + public Uri? InteractionUri { get; set; } + + /// + /// The URL to initiate the login process. This URI is typically used in scenarios where the OIDC server needs to + /// direct users to a specific login interface or when integrating with external identity providers. Configuring + /// this URI allows the OIDC server to delegate the initial user authentication step to another service or UI. + /// + public Uri? LoginUri { get; set; } + + /// + /// The name of the parameter used by the OIDC server to pass the authorization request identifier. This parameter + /// name is used in URLs and requests to reference specific authorization requests, especially in advanced features + /// like Pushed Authorization Requests (PAR). Customizing this parameter name can help align with specific client + /// requirements or naming conventions. + /// + public string RequestUriParameterName { get; set; } = AuthorizationRequest.Parameters.RequestUri; + + /// + /// Specifies which OIDC endpoints are enabled on the server. This property allows for fine-grained control over + /// the available functionality, enabling or disabling specific endpoints based on the server's role, security + /// considerations, or operational requirements. By default, all endpoints are enabled. + /// + public OidcEndpoints EnabledEndpoints { get; set; } = OidcEndpoints.All; + + /// + /// The collection of JSON Web Keys (JWK) used for signing tokens issued by the OIDC server. + /// Signing tokens is a critical security measure that ensures the integrity and authenticity of the tokens. + /// These keys are used to digitally sign ID tokens, access tokens, and other JWTs issued by the server, + /// allowing clients to verify that the tokens have not been tampered with and were indeed issued by this server. + /// It is recommended to rotate these keys periodically to maintain the security of the token signing process. + /// + public IReadOnlyCollection SigningKeys { get; set; } = Array.Empty(); + + /// + /// Options related to the check session mechanism in OIDC. This configuration controls how the OIDC server manages + /// session state information, allowing clients to monitor the login session's status. Properly configuring these + /// options ensures that clients can react to session changes (e.g., logout) in a timely and secure manner. + /// + public CheckSessionCookieOptions CheckSessionCookie { get; set; } = new(); + + /// + /// The duration after which a login session expires. This setting determines how long a user's authentication + /// session remains valid before requiring re-authentication. Configuring this duration is essential for balancing + /// security concerns with usability, particularly in environments with varying security requirements. + /// + public TimeSpan LoginSessionExpiresIn { get; set; } = TimeSpan.FromMinutes(10); + + /// + /// Configuration options for registering new clients dynamically in the OIDC server. These options define default + /// values and constraints for new client registrations, facilitating dynamic and secure client onboarding processes. + /// + public NewClientOptions NewClientOptions { get; init; } = new(); + + /// + /// The collection of JSON Web Keys (JWK) used for encrypting tokens or sensitive information sent to the clients. + /// Encryption is essential for protecting sensitive data within tokens, especially when tokens are passed through + /// less secure channels or when storing tokens at the client side. These keys are utilized to encrypt ID tokens and, + /// optionally, access tokens when the OIDC server sends them to clients. Clients use the corresponding public keys + /// to decrypt the tokens and access the contained claims. + /// + public IReadOnlyCollection EncryptionKeys { get; set; } = Array.Empty(); + + /// + /// The duration for which a Pushed Authorization Request (PAR) is valid. PAR is a security enhancement that allows + /// clients to pre-register authorization requests directly with the authorization server. This duration specifies + /// the maximum time a pre-registered request is considered valid, balancing the need for security with usability + /// in completing the authorization process. + /// + public TimeSpan PushedAuthorizationRequestExpiresIn { get; set; } = TimeSpan.FromMinutes(1); + + /// + /// A JWT used for licensing and configuration validation of the OIDC service. This token contains claims that the + /// OIDC service uses to validate its configuration, features, and licensing status, ensuring the service operates + /// within its licensed capabilities. Proper validation of this token is crucial for the service's legal and functional + /// compliance. + /// + public string? LicenseJwt { get; set; } + + public int AuthorizationCodeLength { get; set; } = 64; + + public int RequestUriLength { get; set; } = 64; +} diff --git a/Abblix.Oidc.Server/Common/Configuration/RefreshTokenOptions.cs b/Abblix.Oidc.Server/Common/Configuration/RefreshTokenOptions.cs new file mode 100644 index 00000000..6b5ed9ad --- /dev/null +++ b/Abblix.Oidc.Server/Common/Configuration/RefreshTokenOptions.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Configuration; + +/// +/// Contains the refresh token options. +/// +public record struct RefreshTokenOptions() +{ + /// + /// Sets the absolute period of expiration for refresh tokens. + /// + public TimeSpan AbsoluteExpiresIn { get; init; } = TimeSpan.FromDays(30); + + /// + /// Sets the sliding (call-to-call relative) period of expiration for refresh tokens. + /// + public TimeSpan? SlidingExpiresIn { get; init; } = TimeSpan.FromDays(15); + + /// + /// Allows to reuse refresh tokens after the first usage. + /// + public bool AllowReuse { get; init; } = true; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ApplicationTypes.cs b/Abblix.Oidc.Server/Common/Constants/ApplicationTypes.cs new file mode 100644 index 00000000..68be93f2 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ApplicationTypes.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Defines constants for different types of applications in OAuth 2.0 and OpenID Connect contexts. +/// +public static class ApplicationTypes +{ + /// + /// Represents a native application type. + /// This type is typically used for applications installed on a device, such as mobile apps or desktop applications. + /// + public const string Native = "native"; + + /// + /// Represents a web application type. + /// This type is used for applications that are accessed through a web browser and typically hosted on a web server. + /// + public const string Web = "web"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ClientAssertionTypes.cs b/Abblix.Oidc.Server/Common/Constants/ClientAssertionTypes.cs new file mode 100644 index 00000000..ea63dc93 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ClientAssertionTypes.cs @@ -0,0 +1,34 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Provides constants for client assertion types used in OAuth 2.0. +/// +public static class ClientAssertionTypes +{ + /// + /// Represents the JWT Bearer client assertion type. + /// + public const string JwtBearer = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ClientAuthenticationMethods.cs b/Abblix.Oidc.Server/Common/Constants/ClientAuthenticationMethods.cs new file mode 100644 index 00000000..c4c9dfa6 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ClientAuthenticationMethods.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// This class defines various client authentication methods used in OAuth 2.0. +/// +public static class ClientAuthenticationMethods +{ + /// + /// Client authenticates with the authorization server using the client ID and secret via HTTP Basic Authentication. + /// + public const string ClientSecretBasic = "client_secret_basic"; + + /// + /// Similar to ClientSecretBasic, but the client secret is sent in the request body. + /// + public const string ClientSecretPost = "client_secret_post"; + + /// + /// The client uses a JWT (JSON Web Token) as a client assertion to authenticate. + /// + public const string ClientSecretJwt = "client_secret_jwt"; + + /// + /// Similar to ClientSecretJwt, but it uses a private key to sign the JWT. + /// + public const string PrivateKeyJwt = "private_key_jwt"; + + /// + /// Indicates that no client authentication is for the OAuth request. + /// + public const string None = "none"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ClientType.cs b/Abblix.Oidc.Server/Common/Constants/ClientType.cs new file mode 100644 index 00000000..07d93407 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ClientType.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Enumeration representing the type of OAuth 2.0 client. +/// +public enum ClientType +{ + /// + /// Represents a public client that does not have a client secret. + /// + Public, + + /// + /// Represents a confidential client that has a client secret. + /// + Confidential, +} diff --git a/Abblix.Oidc.Server/Common/Constants/CodeChallengeMethods.cs b/Abblix.Oidc.Server/Common/Constants/CodeChallengeMethods.cs new file mode 100644 index 00000000..77176a0f --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/CodeChallengeMethods.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Static class representing the methods for PKCE (Proof Key for Code Exchange) code challenges. +/// +public static class CodeChallengeMethods +{ + /// + /// Represents the "plain" code challenge method where the code verifier is sent without hashing. + /// + public const string Plain = "plain"; + + /// + /// Represents the "S256" code challenge method where the code verifier is hashed using SHA-256. + /// + public const string S256 = "S256"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/DisplayModes.cs b/Abblix.Oidc.Server/Common/Constants/DisplayModes.cs new file mode 100644 index 00000000..81f0b758 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/DisplayModes.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// The class representing the display modes for the authentication and consent UI. +/// +public static class DisplayModes +{ + /// + /// The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. + /// If the display parameter is not specified, this is the default display mode. + /// + public const string Page = "page"; + + /// + /// The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. + /// The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure + /// the entire window that it is popping up over. + /// + public const string Popup = "popup"; + + /// + /// The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. + /// + public const string Touch = "touch"; + + /// + /// The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display. + /// + public const string Wap = "wap"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ErrorCodes.cs b/Abblix.Oidc.Server/Common/Constants/ErrorCodes.cs new file mode 100644 index 00000000..0765f144 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ErrorCodes.cs @@ -0,0 +1,190 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents OAuth 2.0 and OpenID Connect error codes. +/// +public static class ErrorCodes +{ + #region RFC 6749: OAuth 2.0 error codes + + /// + /// The request is missing a parameter, includes an unsupported parameter value (other than grant type), + /// repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the + /// client, or is otherwise malformed. + /// + public const string InvalidRequest = "invalid_request"; + + /// + /// Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). + /// The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. + /// If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an + /// HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client. + /// + public const string InvalidClient = "invalid_client"; + + /// + /// The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, + /// does not match the redirection URI used in the authorization request, or was issued to another client. + /// + public const string InvalidGrant = "invalid_grant"; + + /// + /// The authenticated client is not authorized to use this authorization grant type. + /// + public const string UnauthorizedClient = "unauthorized_client"; + + /// + /// The authorization grant type is not supported by the authorization server. + /// + public const string UnsupportedGrantType = "unsupported_grant_type"; + + /// + /// The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. + /// + public const string InvalidScope = "invalid_scope"; + + /// + /// The resource owner or authorization server denied the request. + /// + public const string AccessDenied = "access_denied"; + + /// + /// The authorization server does not support obtaining a response using this method. + /// + public const string UnsupportedResponseType = "unsupported_response_type"; + + /// + /// The authorization server encountered an unexpected condition that prevented it from fulfilling the request. + /// + /// + /// This error code is needed because a 500 Internal Server Error HTTP status code cannot be returned to the client via an HTTP redirect. + /// + public const string ServerError = "server_error"; + + /// + /// The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server. + /// + /// + /// This error code is needed because a 500 Internal Server Error HTTP status code cannot be returned to the client via an HTTP redirect. + /// + public const string TemporarilyUnavailable = "temporarily_unavailable"; + + #endregion + + #region OpenID Connect Core error codes + + /// + /// The Authorization Server requires End-User interaction of some form to proceed. This error MAY be returned when the prompt parameter value in + /// the Authentication Request is none, but the Authentication Request cannot be completed without displaying a user interface for End-User interaction. + /// + public const string InteractionRequired = "interaction_required"; + + /// + /// The Authorization Server requires End-User authentication. This error MAY be returned when the prompt parameter value in the Authentication Request + /// is none, but the Authentication Request cannot be completed without displaying a user interface for End-User authentication. + /// + public const string LoginRequired = "login_required"; + + /// + /// The End-User is REQUIRED to select a session at the Authorization Server. The End-User MAY be authenticated at the Authorization Server with + /// different associated accounts, but the End-User did not select a session. + /// This error MAY be returned when the prompt parameter value in the Authentication Request is none, but the Authentication Request cannot be completed + /// without displaying a user interface to prompt for a session to use. + /// + public const string AccountSelectionRequired = "account_selection_required"; + + /// + /// The Authorization Server requires End-User consent. This error MAY be returned when the prompt parameter value in the Authentication Request is none, + /// but the Authentication Request cannot be completed without displaying a user interface for End-User consent. + /// + public const string ConsentRequired = "consent_required"; + + /// + /// The request_uri in the Authorization Request returns an error or contains invalid data. + /// + public const string InvalidRequestUri = "invalid_request_uri"; + + /// + /// The request parameter contains an invalid Request Object. + /// + public const string InvalidRequestObject = "invalid_request_object"; + + /// + /// The OpenId Provider does not support use of the request parameter defined in Section 6: + /// https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests + /// + public const string RequestNotSupported = "request_not_supported"; + + /// + /// The OpenId Provider does not support use of the request_uri parameter defined in Section 6: + /// https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests + /// + public const string RequestUriNotSupported = "request_uri_not_supported"; + + /// + /// The OpenId Provider does not support use of the registration parameter defined in Section 7.2.1: + /// https://openid.net/specs/openid-connect-core-1_0.html#RegistrationParameter + /// + public const string RegistrationNotSupported = "registration_not_supported"; + + #endregion + + #region RFC 7009: OAuth 2.0 Token Revocation + + /// + /// The authorization server does not support the revocation of the presented token type. + /// That is, the client tried to revoke an access token on a server not supporting this feature. + /// + public const string UnsupportedTokenType = "unsupported_token_type"; + + #endregion + + #region OpenID Connect Dynamic Client Registration 1.0 + + /// + /// The value of one or more redirect_uris is invalid. + /// + public const string InvalidRedirectUri = "invalid_redirect_uri"; + + /// + /// The value of one of the Client Metadata fields is invalid and the server has rejected this request. + /// + /// + /// Note that an Authorization Server MAY choose to substitute a valid value for any requested parameter of a Client's Metadata. + /// + public const string InvalidClientMetadata = "invalid_client_metadata"; + + #endregion + + /// + /// The request requires additional confirmation from the resource owner or authorization server. + /// + public const string ConfirmationRequired = "confirmation_required"; + + /// + /// The target resource or identifier provided in the request is invalid. + /// + public const string InvalidTarget = "invalid_target"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/FlowTypes.cs b/Abblix.Oidc.Server/Common/Constants/FlowTypes.cs new file mode 100644 index 00000000..1c7509a2 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/FlowTypes.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents OAuth 2.0 flow types. +/// +[Flags] +public enum FlowTypes +{ + /// + /// When using the Authorization Code Flow, all tokens are returned from the Token Endpoint. + /// The Authorization Code Flow returns an Authorization Code to the Client, which can then exchange it for an ID Token and an Access Token directly. + /// This provides the benefit of not exposing any tokens to the User Agent and possibly other malicious applications with access to the User Agent. + /// The Authorization Server can also authenticate the Client before exchanging the Authorization Code for an Access Token. + /// The Authorization Code flow is suitable for Clients that can securely maintain a Client Secret between themselves and the Authorization Server. + /// + /// https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth + AuthorizationCode = 1 << 0, + + /// + /// When using the Implicit Flow, all tokens are returned from the Authorization Endpoint; the Token Endpoint is not used. + /// The Implicit Flow is mainly used by Clients implemented in a browser using a scripting language. + /// The Access Token and ID Token are returned directly to the Client, which may expose them to the End-User and applications + /// that have access to the End-User's User Agent. The Authorization Server does not perform Client Authentication. + /// + /// https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth + Implicit = 1 << 1, + + /// + /// When using the Hybrid Flow, some tokens are returned from the Authorization Endpoint and others are returned from the Token Endpoint. + /// The mechanisms for returning tokens in the Hybrid Flow are specified in OAuth 2.0 Multiple Response Type Encoding Practices. + /// + /// https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowSteps + Hybrid = AuthorizationCode | Implicit, +} diff --git a/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs b/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs new file mode 100644 index 00000000..f39cf184 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs @@ -0,0 +1,79 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents OAuth 2.0 grant types. +/// +public static class GrantTypes +{ + /// + /// Represents the Authorization Code grant type. Used when a client wants to exchange an authorization code + /// for an access token. Commonly used in web applications with server-side backends. + /// + public const string AuthorizationCode = "authorization_code"; + + /// + /// Represents the Client Credentials grant type. Used when a client requests an access token using its + /// own credentials. Suitable for machine-to-machine communication. + /// + public const string ClientCredentials = "client_credentials"; + + /// + /// Represents the Refresh Token grant type. Used to obtain a new access token using a refresh token. + /// Helpful for maintaining user sessions without requiring reauthentication. + /// + public const string RefreshToken = "refresh_token"; + + /// + /// Represents the Implicit grant type. Used in single-page applications to obtain access tokens directly + /// from the authorization endpoint. Suitable for browser-based applications. + /// + public const string Implicit = "implicit"; + + /// + /// Represents the Password grant type. Allows clients to exchange a username and password for an access token. + /// Should be used with caution due to potential security risks. + /// + public const string Password = "password"; + + /// + /// Represents the CIBA (Client Initiated Backchannel Authentication) grant type. + /// Used for authentication with minimal user interaction, often in use cases like strong customer authentication. + /// + public const string Ciba = "urn:openid:params:grant-type:ciba"; + + /// + /// Represents the JWT Bearer grant type. Allows clients to request access tokens using a JWT + /// (JSON Web Token) assertion. Useful for securing API-to-API communication. + /// + public const string JwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"; + + /// + /// Represents the Device Authorization grant type. This grant type is used in scenarios where the client device + /// lacks a browser or has limited input capabilities, allowing it to obtain user authorization from another device + /// with better input capabilities. It is particularly useful for devices in the IoT (Internet of Things) sector + /// and smart devices that require user interaction for authorization. + /// + public const string DeviceAuthorization = "urn:ietf:params:oauth:grant-type:device_code"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/HttpRequestHeaders.cs b/Abblix.Oidc.Server/Common/Constants/HttpRequestHeaders.cs new file mode 100644 index 00000000..5db0a893 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/HttpRequestHeaders.cs @@ -0,0 +1,35 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// This class defines HTTP header names commonly used in HTTP requests. +/// +public static class HttpRequestHeaders +{ + /// + /// The "Authorization" header is used in HTTP requests to include authentication credentials. + /// It is crucial for securing API endpoints and providing proof of client identity or permissions. + /// + public const string Authorization = "Authorization"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/JwtTypes.cs b/Abblix.Oidc.Server/Common/Constants/JwtTypes.cs new file mode 100644 index 00000000..a737b7f3 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/JwtTypes.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// This class defines JWT (JSON Web Token) types used in various contexts. +/// +public static class JwtTypes +{ + /// + /// The "AccessToken" JWT type is used to represent access tokens, typically used for authenticating and authorizing users in APIs. + /// + public const string AccessToken = "at+jwt"; + + /// + /// The "LogoutToken" JWT type is used in the context of OpenID Connect for single logout functionality. + /// + public const string LogoutToken = "logout+jwt"; + + /// + /// The "RefreshToken" JWT type is used to represent refresh tokens, which allow obtaining new access tokens without reauthentication. + /// + public const string RefreshToken = "refresh+jwt"; + + /// + /// The "RegistrationAccessToken" JWT type is used in OAuth 2.0 Dynamic Client Registration for securely registering clients. + /// + public const string RegistrationAccessToken = "registration+jwt"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/MediaTypes.cs b/Abblix.Oidc.Server/Common/Constants/MediaTypes.cs new file mode 100644 index 00000000..27074845 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/MediaTypes.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents common media types used in HTTP requests and responses. +/// +public static class MediaTypes +{ + /// + /// Represents the "application/x-www-form-urlencoded" media type for HTML form data. + /// + public const string FormUrlEncoded = "application/x-www-form-urlencoded"; + + /// + /// Represents the "application/jwt" media type for JSON Web Tokens (JWT). + /// + public const string Jwt = "application/jwt"; + + /// + /// Represents the "text/javascript" media type for JavaScript code. + /// + public const string Javascript = "text/javascript"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/Prompts.cs b/Abblix.Oidc.Server/Common/Constants/Prompts.cs new file mode 100644 index 00000000..1b77d3c9 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/Prompts.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +public static class Prompts +{ + /// + /// This prompt indicates that the Authorization Server MUST NOT display any authentication or consent user + /// interface pages. An error is returned if an End-User is not already authenticated or the Client does not have + /// pre-configured consent for the requested Claims or does not fulfill other conditions for processing the request. + /// + public const string None = "none"; + + /// + /// The Authorization Server SHOULD prompt the End-User for re-authentication. + /// + public const string Login = "login"; + + /// + /// The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client. + /// + public const string Consent = "consent"; + + /// + /// The Authorization Server SHOULD prompt the End-User to select a user account. + /// This enables an End-User who has multiple accounts at the Authorization Server to select amongst + /// the multiple accounts that they might have current sessions for. + /// + public const string SelectAccount = "select_account"; + + /// + /// This prompt indicates that the Authorization Server SHOULD prompt the End-User to create a new account. + /// This is generally used for applications that include user registration as part of the authorization process. + /// If the Authorization Server cannot proceed with account creation, it MUST return an appropriate error, typically interaction_required. + /// + public const string Create = "create"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/RequestUrn.cs b/Abblix.Oidc.Server/Common/Constants/RequestUrn.cs new file mode 100644 index 00000000..72405b1b --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/RequestUrn.cs @@ -0,0 +1,34 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Provides constants for OAuth request URIs, ensuring they conform to the standardized URN notation. +/// +public static class RequestUrn +{ + /// + /// The prefix for OAuth request URIs as per the Internet Engineering Task Force (IETF) parameters. + /// + public const string Prefix = "urn:ietf:params:oauth:request_uri:"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ResponseModes.cs b/Abblix.Oidc.Server/Common/Constants/ResponseModes.cs new file mode 100644 index 00000000..c669160c --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ResponseModes.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents common response modes used in OAuth 2.0 and OpenID Connect flows. +/// +public static class ResponseModes +{ + /// + /// Represents the "form_post" response mode, where the response parameters are encoded as HTML form values and + /// sent as a POST request to the redirect URI. + /// + public const string FormPost = "form_post"; + + /// + /// Represents the "query" response mode, where the response parameters are appended as query parameters to the + /// redirect URI. + /// + public const string Query = "query"; + + /// + /// Represents the "fragment" response mode, where the response parameters are appended as URL fragments to the + /// redirect URI. + /// + public const string Fragment = "fragment"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ResponseTypes.cs b/Abblix.Oidc.Server/Common/Constants/ResponseTypes.cs new file mode 100644 index 00000000..7bb9591c --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ResponseTypes.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents common response types used in OAuth 2.0 and OpenID Connect flows. +/// +/// Reference: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html +public static class ResponseTypes +{ + /// + /// Represents the "none" response type, indicating no specific response type is requested. + /// This is typically used when the client does not expect a response or when only error handling is needed. + /// + public const string None = "none"; + + /// + /// Represents the "code" response type, indicating the authorization code response type. + /// This is used in the Authorization Code Flow to request an authorization code for later exchange. + /// + public const string Code = "code"; + + /// + /// Represents the "token" response type, indicating the token response type. + /// This is used in Implicit Flow to directly issue tokens to the client without using an authorization code. + /// + public const string Token = "token"; + + /// + /// Represents the "id_token" response type, indicating the ID token response type. + /// This is used to request only an ID token in the response, typically in OpenID Connect scenarios. + /// + public const string IdToken = "id_token"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/ScopeDefinition.cs b/Abblix.Oidc.Server/Common/Constants/ScopeDefinition.cs new file mode 100644 index 00000000..a0c709fb --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/ScopeDefinition.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Defines a structure for OAuth 2.0 scope definitions, specifying the scope and associated claim types. +/// +public record ScopeDefinition(string Scope, params string[] ClaimTypes); diff --git a/Abblix.Oidc.Server/Common/Constants/Scopes.cs b/Abblix.Oidc.Server/Common/Constants/Scopes.cs new file mode 100644 index 00000000..be3b9418 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/Scopes.cs @@ -0,0 +1,62 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents common OAuth 2.0 and OpenID Connect scopes. +/// +public static class Scopes +{ + /// + /// The "openid" scope is used to indicate that the request is an OpenID Connect request, + /// allowing the identity of the user to be included in the response. + /// + public const string OpenId = "openid"; + + /// + /// The "profile" scope is used to request access to the user's profile information, + /// such as their name, picture, and other profile-related details. + /// + public const string Profile = "profile"; + + /// + /// The "email" scope is used to request access to the user's email address. + /// + public const string Email = "email"; + + /// + /// The "phone" scope is used to request access to the user's phone number. + /// + public const string Phone = "phone"; + + /// + /// The "offline_access" scope is used to request a refresh token that allows the client + /// to obtain new access tokens without user interaction. + /// + public const string OfflineAccess = "offline_access"; + + /// + /// The "address" scope is used to request access to the user's physical address information. + /// + public const string Address = "address"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/StandardScopes.cs b/Abblix.Oidc.Server/Common/Constants/StandardScopes.cs new file mode 100644 index 00000000..e0075c16 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/StandardScopes.cs @@ -0,0 +1,87 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Provides definitions for standard OpenID Connect scopes and their associated claims. +/// +public static class StandardScopes +{ + /// + /// Represents the 'openid' scope, which is essential for OpenID Connect processes. + /// + public static readonly ScopeDefinition OpenId = new( + Scopes.OpenId, + IanaClaimTypes.Sub); + + /// + /// Represents the 'profile' scope, including claims about the end-user's profile information. + /// + public static readonly ScopeDefinition Profile = new( + Scopes.Profile, + IanaClaimTypes.Name, + IanaClaimTypes.FamilyName, + IanaClaimTypes.GivenName, + IanaClaimTypes.MiddleName, + IanaClaimTypes.Nickname, + IanaClaimTypes.PreferredUsername, + IanaClaimTypes.Profile, + IanaClaimTypes.Picture, + IanaClaimTypes.Website, + IanaClaimTypes.Gender, + IanaClaimTypes.Birthdate, + IanaClaimTypes.Zoneinfo, + IanaClaimTypes.Locale, + IanaClaimTypes.UpdatedAt); + + /// + /// Represents the 'address' scope, including claims about the end-user's physical address. + /// + public static readonly ScopeDefinition Address = new( + Scopes.Address, + IanaClaimTypes.Address); + + /// + /// Represents the 'email' scope, including claims about the end-user's email address and verification status. + /// + public static readonly ScopeDefinition Email = new( + Scopes.Email, + IanaClaimTypes.Email, + IanaClaimTypes.EmailVerified); + + /// + /// Represents the 'phone' scope, including claims about the end-user's phone number and verification status. + /// + public static readonly ScopeDefinition Phone = new( + Scopes.Phone, + IanaClaimTypes.PhoneNumber, + IanaClaimTypes.PhoneNumberVerified); + + /// + /// Represents the 'offline_access' scope, which allows the client to request refresh tokens for long-term access. + /// + public static readonly ScopeDefinition OfflineAccess = new( + Scopes.OfflineAccess); +} diff --git a/Abblix.Oidc.Server/Common/Constants/SubjectTypes.cs b/Abblix.Oidc.Server/Common/Constants/SubjectTypes.cs new file mode 100644 index 00000000..0796b4fd --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/SubjectTypes.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents subject types used in OpenID Connect. +/// +public static class SubjectTypes +{ + /// + /// The "public" subject type indicates that the subject identifier is a public identifier, + /// which means that it can be used across multiple clients and should not be tied to a specific client. + /// + public const string Public = "public"; + + /// + /// The "pairwise" subject type indicates that the subject identifier is a pairwise identifier, + /// which means that it is unique to a specific client, enhancing user privacy. + /// + public const string Pairwise = "pairwise"; +} diff --git a/Abblix.Oidc.Server/Common/Constants/TokenTypeIdentifiers.cs b/Abblix.Oidc.Server/Common/Constants/TokenTypeIdentifiers.cs new file mode 100644 index 00000000..b8891d6f --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/TokenTypeIdentifiers.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents token type identifiers for various token types as specified in RFC 8693. +/// +/// +/// See https://datatracker.ietf.org/doc/html/rfc8693#TokenTypeIdentifiers for details. +/// +public static class TokenTypeIdentifiers +{ + /// + /// Indicates that the token is an OAuth 2.0 access token issued by the given authorization server. + /// + public static readonly Uri AccessToken = new("urn:ietf:params:oauth:token-type:access_token"); + + /// + /// Indicates that the token is an OAuth 2.0 refresh token issued by the given authorization server. + /// + public static readonly Uri RefreshToken = new("urn:ietf:params:oauth:token-type:refresh_token"); + + /// + /// Indicates that the token is an ID Token as defined in Section 2 of https://openid.net/specs/openid-connect-core-1_0.html. + /// + public static readonly Uri IdToken = new("urn:ietf:params:oauth:token-type:id_token"); + + /// + /// Indicates that the token is a base64url-encoded SAML 1.1 https://www.oasis-open.org/committees/download.php/3406/oasis-sstc-saml-core-1.1.pdf assertion. + /// + public static readonly Uri Saml1 = new("urn:ietf:params:oauth:token-type:saml1"); + + /// + /// Indicates that the token is a base64url-encoded SAML 2.0 [http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf] assertion. + /// + public static readonly Uri Saml2 = new("urn:ietf:params:oauth:token-type:saml2"); +} diff --git a/Abblix.Oidc.Server/Common/Constants/TokenTypes.cs b/Abblix.Oidc.Server/Common/Constants/TokenTypes.cs new file mode 100644 index 00000000..18e2218a --- /dev/null +++ b/Abblix.Oidc.Server/Common/Constants/TokenTypes.cs @@ -0,0 +1,34 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Constants; + +/// +/// Represents token types used in authentication and authorization. +/// +public static class TokenTypes +{ + /// + /// Indicates the Bearer token type, often used in OAuth 2.0 for securing API requests. + /// + public const string Bearer = "Bearer"; +} diff --git a/Abblix.Oidc.Server/Common/Cookie.cs b/Abblix.Oidc.Server/Common/Cookie.cs new file mode 100644 index 00000000..2a0fa4e5 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Cookie.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common; + +/// +/// Represents an HTTP cookie along with its options. +/// +public record Cookie(string Name, CookieOptions Options); diff --git a/Abblix.Oidc.Server/Common/CookieOptions.cs b/Abblix.Oidc.Server/Common/CookieOptions.cs new file mode 100644 index 00000000..7f6ec5fb --- /dev/null +++ b/Abblix.Oidc.Server/Common/CookieOptions.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common; + +/// +/// Represents options for an HTTP cookie, including properties for HTTP-only, essential, secure, +/// path, domain, SameSite attribute, expiration, and maximum age. +/// +public record CookieOptions +{ + /// + /// Indicates if the cookie is accessible only through HTTP. + /// + public bool HttpOnly { get; set; } + + /// + /// Indicates if the cookie is essential for the application's functionality. + /// + public bool IsEssential { get; set; } + + /// + /// Indicates if the cookie should only be sent over secure channels (HTTPS). + /// + public bool Secure { get; set; } + + /// + /// The path for which the cookie is valid. + /// + public string? Path { get; set; } + + /// + /// The domain for which the cookie is valid. + /// + public string? Domain { get; set; } + + /// + /// The SameSite attribute of the cookie. + /// + public string? SameSite { get; set; } + + /// + /// The expiration date and time of the cookie. + /// + public DateTimeOffset? Expires { get; set; } + + /// + /// The maximum age of the cookie as a time span. + /// + public TimeSpan? MaxAge { get; set; } +} diff --git a/Abblix.Oidc.Server/Common/Exceptions/UnexpectedTypeException.cs b/Abblix.Oidc.Server/Common/Exceptions/UnexpectedTypeException.cs new file mode 100644 index 00000000..bc30ab69 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Exceptions/UnexpectedTypeException.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Exceptions; + +/// +/// Represents an exception that is thrown when an unexpected data type is encountered. +/// +/// +/// This exception is typically used to indicate an unexpected or invalid type for a parameter or variable. +/// It provides information about the parameter name and the unexpected type encountered. +/// +public class UnexpectedTypeException : InvalidOperationException +{ + public UnexpectedTypeException(string? paramName, Type paramType) + : base($"Something goes wrong: {paramName} has unexpected type {paramType}") + { + } +} diff --git a/Abblix.Oidc.Server/Common/Implementation/JsonBinarySerializer.cs b/Abblix.Oidc.Server/Common/Implementation/JsonBinarySerializer.cs new file mode 100644 index 00000000..89a096eb --- /dev/null +++ b/Abblix.Oidc.Server/Common/Implementation/JsonBinarySerializer.cs @@ -0,0 +1,67 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text; +using System.Text.Json; +using Abblix.Oidc.Server.Common.Interfaces; + +namespace Abblix.Oidc.Server.Common.Implementation; + +/// +/// Provides functionality to serialize and deserialize objects to and from JSON binary representations. +/// Implements the interface using the System.Text.Json library for serialization. +/// +public class JsonBinarySerializer : IBinarySerializer +{ + public JsonBinarySerializer(Encoding? encoding = null, JsonSerializerOptions? options = null) + { + _encoding = encoding ?? Encoding.UTF8; + _options = options; + } + + private readonly Encoding _encoding; + private readonly JsonSerializerOptions? _options; + + /// + /// Serializes an object to a binary representation in JSON format. + /// + /// The type of the object to serialize. + /// The object to serialize into JSON format. + /// A byte array representing the serialized object in JSON format. + public byte[] Serialize(T obj) + { + var json = JsonSerializer.Serialize(obj, _options); + return _encoding.GetBytes(json); + } + + /// + /// Deserializes a binary representation of a JSON object to its original type. + /// + /// The type of the object to deserialize into. + /// The binary representation of the JSON object to deserialize. + /// The deserialized object of type . + public T? Deserialize(byte[] bytes) + { + var json = _encoding.GetString(bytes); + return JsonSerializer.Deserialize(json, _options); + } +} diff --git a/Abblix.Oidc.Server/Common/Implementation/JsonSerializationBinder.cs b/Abblix.Oidc.Server/Common/Implementation/JsonSerializationBinder.cs new file mode 100644 index 00000000..4bdce492 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Implementation/JsonSerializationBinder.cs @@ -0,0 +1,112 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Nodes; +using Abblix.Oidc.Server.Common.Interfaces; + +namespace Abblix.Oidc.Server.Common.Implementation; + + +/// +/// Implements the interface to bind JSON data from a +/// to a specified model type. This binder utilizes System.Text.Json for serialization to dynamically bind +/// the JSON data to the model's properties, allowing for both creation of new model instances or updating +/// existing ones based on the provided JSON data. +/// +public class JsonSerializationBinder : IJsonObjectBinder +{ + /// + /// Asynchronously binds JSON data from a to a specified model of type + /// . + /// The method can update an existing model instance with the data or create and populate a new instance + /// if none is provided. + /// + /// The type of the model to which the data is to be bound. + /// The JSON data as a containing the properties + /// to bind to the model. + /// An optional instance of the model to be updated. If null, a new instance + /// of + /// is created. + /// A that, when completed, results in the bound model instance + /// of , or null if the binding fails. + /// + /// This method leverages the JSON serialization capabilities of System.Text.Json to map + /// the JSON properties to the corresponding properties of the model. + /// It's designed to handle complex object graphs and can be used to easily populate models + /// from JSON data or update existing models with new data. + /// + public Task BindModelAsync(JsonObject properties, TModel? model) + where TModel : class + { + return Task.FromResult(BindModel(properties, model)); + } + + /// + /// Synchronously binds JSON data from a to a model of type , + /// creating a new instance of the model or updating an existing one. + /// + /// The model type to which the JSON data should be bound. + /// The JSON data as a to bind to the model. + /// An optional model instance to update with the JSON data. + /// If null, a new instance is created. + /// The bound model instance if successful, or null if the binding fails. + /// + /// This method is used internally by to perform + /// the actual binding operation. + /// It merges the JSON data into the provided model instance or creates a new one, + /// utilizing a memory stream to serialize and deserialize the merged JSON data. + /// + private static TModel? BindModel(JsonObject properties, TModel? model) where TModel : class + { + if (model == null) + { + return properties.Deserialize(); + } + + var jsonModel = JsonSerializer.SerializeToElement(model); + var jsonProperties = JsonSerializer.SerializeToElement(properties); + + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) + { + writer.WriteStartObject(); + + // Write properties from the JSON model + foreach (var property in jsonModel.EnumerateObject()) + { + property.WriteTo(writer); + } + + // Write/overwrite properties from the JSON properties to overwrite + foreach (var property in jsonProperties.EnumerateObject()) + { + property.WriteTo(writer); + } + + writer.WriteEndObject(); + } + + stream.Seek(0, SeekOrigin.Begin); + return JsonSerializer.Deserialize(stream); + } +} diff --git a/Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs b/Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs new file mode 100644 index 00000000..b4ce2621 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs @@ -0,0 +1,76 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Utils; +using Microsoft.Extensions.Options; + + +namespace Abblix.Oidc.Server.Common.Implementation; + +/// +/// Provides access to JSON Web Keys (JWK) used for encryption and signing JWT tokens. +/// +/// +/// This implementation provides keys for encryption and signing purposes by mapping X509 certificates to JWK format. +/// It is recommended to implement a dynamic resolution mechanism in production environments +/// to enable seamless certificate replacement without the need for service reloading. +/// +internal class OidcOptionsKeysProvider : IAuthServiceKeysProvider +{ + public OidcOptionsKeysProvider(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Retrieves a collection of JSON Web Keys used for encryption, based on the configured encryption certificates. + /// + /// Specifies whether to include private keys in the JWKs. Default is false. + /// An asynchronous stream of for encryption purposes. + public IAsyncEnumerable GetEncryptionKeys(bool includePrivateKeys) + { + var jsonWebKeys = + from jwk in _options.Value.EncryptionKeys + select jwk.Sanitize(includePrivateKeys); + + return jsonWebKeys.AsAsync(); + } + + /// + /// Retrieves a collection of JSON Web Keys used for signing, based on the configured signing certificates. + /// + /// Specifies whether to include private keys in the JWKs. Default is false. + /// An asynchronous stream of for signing purposes. + public IAsyncEnumerable GetSigningKeys(bool includePrivateKeys) + { + var jsonWebKeys = + from jwk in _options.Value.SigningKeys + select jwk.Sanitize(includePrivateKeys); + + return jsonWebKeys.AsAsync(); + } +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IAuthServiceKeysProvider.cs b/Abblix.Oidc.Server/Common/Interfaces/IAuthServiceKeysProvider.cs new file mode 100644 index 00000000..05e9b053 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IAuthServiceKeysProvider.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Provides the keys of the OpenID Connect service to encrypt and sign JWT tokens issued by it. +/// +public interface IAuthServiceKeysProvider +{ + /// + /// Gets the encryption keys used by the service. + /// + /// Whether to include private keys in the result. + IAsyncEnumerable GetEncryptionKeys(bool includePrivateKeys = false); + + /// + /// Gets the signing keys used by the service. + /// + /// Whether to include private keys in the result. + IAsyncEnumerable GetSigningKeys(bool includePrivateKeys = false); +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IBinarySerializer.cs b/Abblix.Oidc.Server/Common/Interfaces/IBinarySerializer.cs new file mode 100644 index 00000000..33ff0724 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IBinarySerializer.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Defines the contract for a binary serializer that supports serialization and deserialization of objects +/// to and from binary format. +/// +public interface IBinarySerializer +{ + /// + /// Serializes an object of type to a binary array. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// A binary array representing the serialized object. + byte[] Serialize(T obj); + + /// + /// Deserializes a binary array to an object of type . + /// + /// The type of the object to deserialize to. + /// The binary array to deserialize from. + /// The deserialized object of type . + T? Deserialize(byte[] bytes); +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IJsonObjectBinder.cs b/Abblix.Oidc.Server/Common/Interfaces/IJsonObjectBinder.cs new file mode 100644 index 00000000..452bd96b --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IJsonObjectBinder.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Provides a mechanism to bind data from a JsonObject to a model, enabling the conversion +/// of JSON data into a strongly typed object. This interface abstracts the process of mapping +/// JSON properties to a model's properties, facilitating the dynamic population of model instances +/// with data from a JSON source. +/// +public interface IJsonObjectBinder +{ + /// + /// Asynchronously binds data from the provided JsonObject to the specified model type. + /// This method allows for the flexible binding of JSON data to C# objects, supporting + /// both the creation of new instances and the population of existing instances. + /// + /// The type of the model to bind. This type must be a class. + /// The JsonObject containing the data to bind to the model. + /// An optional instance of the model to populate. + /// If null, a new instance of TModel will be created. + /// A task representing the asynchronous operation, + /// which upon completion yields the bound model instance if successful, or null if the binding fails. + Task BindModelAsync(JsonObject properties, TModel? model = null) where TModel : class; +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IParameterValidator.cs b/Abblix.Oidc.Server/Common/Interfaces/IParameterValidator.cs new file mode 100644 index 00000000..5c419273 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IParameterValidator.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics.CodeAnalysis; + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Provides a method for validating that a parameter is and not null. +/// +public interface IParameterValidator +{ + /// + /// Validates that a parameter is and not null. + /// + /// The type of the parameter. + /// The value of the parameter. + /// The name of the parameter. + void Required([NotNull] T? value, string name) where T : class; +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IRequestInfoProvider.cs b/Abblix.Oidc.Server/Common/Interfaces/IRequestInfoProvider.cs new file mode 100644 index 00000000..5d731886 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IRequestInfoProvider.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Provides information about the current request, including URIs and security details. +/// +public interface IRequestInfoProvider +{ + /// + /// Gets the base URI of the application. + /// + string ApplicationUri { get; } + + /// + /// Gets the request URI. + /// + string RequestUri { get; } + + /// + /// Indicates whether the request is using HTTPS. + /// + bool IsHttps { get; } + + /// + /// Gets the base path of the request. + /// + string PathBase { get; } +} diff --git a/Abblix.Oidc.Server/Common/Interfaces/IUserCredentialsAuthenticator.cs b/Abblix.Oidc.Server/Common/Interfaces/IUserCredentialsAuthenticator.cs new file mode 100644 index 00000000..f912bcc9 --- /dev/null +++ b/Abblix.Oidc.Server/Common/Interfaces/IUserCredentialsAuthenticator.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; + + + +namespace Abblix.Oidc.Server.Common.Interfaces; + +/// +/// Represents an interface for authenticating user credentials. +/// +public interface IUserCredentialsAuthenticator +{ + /// + /// Validates user credentials (username and password) and returns a grant authorization result. + /// + /// The username provided by the user. + /// The password provided by the user. + /// The authorization context associated with the request. + /// A task that represents the asynchronous validation operation and returns the grant authorization result. + Task ValidateAsync(string userName, string password, AuthorizationContext context); +} diff --git a/Abblix.Oidc.Server/Common/JsonWebKeyExtensions.cs b/Abblix.Oidc.Server/Common/JsonWebKeyExtensions.cs new file mode 100644 index 00000000..379c8978 --- /dev/null +++ b/Abblix.Oidc.Server/Common/JsonWebKeyExtensions.cs @@ -0,0 +1,50 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Common; + +/// +/// Provides extensions for asynchronous operations on a sequence of objects. +/// +public static class JsonWebKeyExtensions +{ + /// + /// Asynchronously retrieves the first with the specified algorithm from the sequence. + /// + /// The asynchronous sequence of objects. + /// The algorithm to match. Returns null if is provided. + /// The first with the specified algorithm or null if not found. + + public static Task FirstByAlgorithmAsync(this IAsyncEnumerable credentials, string? alg) + { + if (alg == SigningAlgorithms.None) + return Task.FromResult(null); + + if (alg.HasValue()) + credentials = credentials.WhereAsync(key => key.Algorithm == alg); + + return credentials.FirstOrDefaultAsync(); + } +} diff --git a/Abblix.Oidc.Server/Common/StringExtensions.cs b/Abblix.Oidc.Server/Common/StringExtensions.cs new file mode 100644 index 00000000..63ed9d2f --- /dev/null +++ b/Abblix.Oidc.Server/Common/StringExtensions.cs @@ -0,0 +1,72 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Common; + +/// +/// Handy extensions for strings. +/// +internal static class StringExtensions +{ + /// + /// Checks if the array of strings contains a specified flag. + /// + /// The array of strings to check. + /// The flag to search for. + /// True if the flag is found; otherwise, false. + public static bool HasFlag(this string[]? values, string flag) + => values != null && values.Contains(flag, StringComparer.OrdinalIgnoreCase); + + /// + /// Attempts to parse a string into an array of allowed values using a separator. + /// + /// The source string to parse. + /// The array of allowed values. + /// The character separator. + /// The parsed values if successful; otherwise, null. + /// True if parsing is successful; otherwise, false. + public static bool TryParse(this string source, string[] allowedValues, char separator, out string[] values) + { + if (string.IsNullOrEmpty(source)) + { + values = Array.Empty(); + return true; + } + + var sourceValues = source.Split(separator, StringSplitOptions.RemoveEmptyEntries); + var result = new List(sourceValues.Length); + foreach (var sourceValue in sourceValues) + { + var allowedValue = allowedValues.FirstOrDefault(value => string.Equals(value, sourceValue, StringComparison.OrdinalIgnoreCase)); + if (allowedValue == null) + { + values = default!; + return false; + } + + result.Add(allowedValue); + } + + values = result.ToArray(); + return true; + } +} diff --git a/Abblix.Oidc.Server/Common/TimeProvider.cs b/Abblix.Oidc.Server/Common/TimeProvider.cs new file mode 100644 index 00000000..b3471eea --- /dev/null +++ b/Abblix.Oidc.Server/Common/TimeProvider.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +#if !NET8_0_OR_GREATER + +// ReSharper disable once CheckNamespace +namespace System; + +/// +/// Represents a clock that provides the current UTC time based on the system clock. +/// This implementation is intended for use in environments or versions of .NET earlier than 8.0, +/// where access to a more precise or configurable time source is not required. It abstracts the +/// mechanism for obtaining time, allowing for more flexible testing or future changes to time +/// acquisition strategies without altering dependent code. +/// +public abstract class TimeProvider +{ + /// + /// Gets the current UTC time directly from the system clock. + /// This abstract method must be implemented by subclasses to return the current UTC time, + /// allowing different strategies for time retrieval, such as fixed time for testing or + /// alternate time sources. + /// + /// The current UTC time as a . + public abstract DateTimeOffset GetUtcNow(); + + /// + /// Provides a singleton instance of the that retrieves the current time + /// using the system's default clock, specifically . + /// This standard implementation is thread-safe and efficient, suitable for general use across various + /// application components. + /// + public static readonly TimeProvider System = new SystemTimeProvider(); + + /// + /// A private nested class that provides the system time. This class implements the + /// method using the system's clock. + /// + private class SystemTimeProvider : TimeProvider + { + /// + /// Returns the current UTC time from the system's clock. + /// This method directly accesses to provide the current time, + /// ensuring that time retrievals are fast and reflect the actual system time with no adjustments or modifications. + /// + /// The current UTC time as a . + public override DateTimeOffset GetUtcNow() => DateTimeOffset.UtcNow; + } +} + +#endif diff --git a/Abblix.Oidc.Server/DeclarativeValidation/AbsoluteUriAttribute.cs b/Abblix.Oidc.Server/DeclarativeValidation/AbsoluteUriAttribute.cs new file mode 100644 index 00000000..1dd8f73d --- /dev/null +++ b/Abblix.Oidc.Server/DeclarativeValidation/AbsoluteUriAttribute.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.DeclarativeValidation; + +/// +/// An attribute to indicate that a property, field, or parameter should represent an absolute URI. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] +public class AbsoluteUriAttribute : Attribute +{ + /// + /// Initializes a new instance of the class with an optional scheme. + /// + /// The scheme for the absolute URI (e.g., "https"). + public AbsoluteUriAttribute(string? requireScheme = null) + { + RequireScheme = requireScheme; + } + + /// + /// The scheme for the absolute URI. + /// + public string? RequireScheme { get; init; } +} diff --git a/Abblix.Oidc.Server/DeclarativeValidation/AllowedValuesAttribute.cs b/Abblix.Oidc.Server/DeclarativeValidation/AllowedValuesAttribute.cs new file mode 100644 index 00000000..a7f44b34 --- /dev/null +++ b/Abblix.Oidc.Server/DeclarativeValidation/AllowedValuesAttribute.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.DeclarativeValidation; + +/// +/// An attribute to specify allowed values for a property, field, or parameter. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] +public class AllowedValuesAttribute : Attribute +{ + /// + /// Initializes a new instance of the class with the allowed values. + /// + /// The list of allowed values. + public AllowedValuesAttribute(params string[] allowedValues) + { + AllowedValues = allowedValues; + } + + /// + /// The list of allowed values. + /// + public string[] AllowedValues { get; } +} diff --git a/Abblix.Oidc.Server/DeclarativeValidation/ElementsRequiredAttribute.cs b/Abblix.Oidc.Server/DeclarativeValidation/ElementsRequiredAttribute.cs new file mode 100644 index 00000000..87a594f0 --- /dev/null +++ b/Abblix.Oidc.Server/DeclarativeValidation/ElementsRequiredAttribute.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.DeclarativeValidation; + +/// +/// An attribute indicating that elements of array (properties, fields, or parameters) are required. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] +public class ElementsRequiredAttribute : Attribute +{ +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationHandler.cs b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationHandler.cs new file mode 100644 index 00000000..233ba283 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationHandler.cs @@ -0,0 +1,105 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization; + +/// +/// Handles the processing of authorization requests by validating and then processing these requests based +/// on defined business logic. It also includes the fetching of authorization requests when necessary. +/// +public class AuthorizationHandler : IAuthorizationHandler +{ + /// + /// Initializes a new instance of the class with the specified request fetcher, + /// validator and processor. + /// + /// The service responsible for fetching external authorization requests when + /// specified by a request or request_uri parameter. + /// The service responsible for validating authorization requests. + /// The service responsible for processing validated authorization requests. + public AuthorizationHandler( + IAuthorizationRequestFetcher fetcher, + IAuthorizationRequestValidator validator, + IAuthorizationRequestProcessor processor) + { + _fetcher = fetcher; + _validator = validator; + _processor = processor; + } + + private readonly IAuthorizationRequestFetcher _fetcher; + private readonly IAuthorizationRequestValidator _validator; + private readonly IAuthorizationRequestProcessor _processor; + + public AuthorizationEndpointMetadata Metadata => new() + { + RequestParameterSupported = true, + ClaimsParameterSupported = true, + }; + + /// + /// Asynchronously handles an authorization request by first fetching the request if necessary, + /// validating the request and then processing it to generate an authorization response. + /// + /// The authorization request to be handled. This can be a direct request or a reference + /// to an external request that needs to be fetched. + /// A task that represents the asynchronous operation, resulting in an . + /// This response can be either an authorization success response or an error response based on the fetching, + /// validation and processing outcomes. + /// Thrown if the validation result is of an unexpected type. + /// + /// The handling process involves three main steps: + /// 1. Fetching of the authorization request if specified by a request or request_uri parameter. + /// 2. Validation of the authorization request against predefined criteria to ensure its legitimacy and completeness. + /// 3. Processing of the validated request to generate an authorization response, which could involve user + /// authentication, consent handling, and token issuance. + /// + /// This method ensures that only requests meeting the necessary validation criteria are processed, + /// maintaining the integrity and security of the authorization flow. + /// + public async Task HandleAsync(AuthorizationRequest request) + { + var fetchResult = await _fetcher.FetchAsync(request); + switch (fetchResult) + { + case FetchResult.Success success: + request = success.Request; + break; + + case FetchResult.Fault { Error: var error }: + return new AuthorizationError(request, error); + } + + var validationResult = await _validator.ValidateAsync(request); + return validationResult switch + { + ValidAuthorizationRequest validRequest => await _processor.ProcessAsync(validRequest), + AuthorizationRequestValidationError error => new AuthorizationError(request, error), + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()), + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestProcessor.cs new file mode 100644 index 00000000..aa84dda4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestProcessor.cs @@ -0,0 +1,230 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.Consents; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using AuthorizationResponse = Abblix.Oidc.Server.Endpoints.Authorization.Interfaces.AuthorizationResponse; + + +namespace Abblix.Oidc.Server.Endpoints.Authorization; + +/// +/// Processes authorization requests by coordinating with various services like authentication, +/// consent, and token issuance. This class handles the logic of determining the appropriate +/// response to an authorization request based on the request's parameters and the current state +/// of the user's session. +/// +public class AuthorizationRequestProcessor : IAuthorizationRequestProcessor +{ + /// + /// Initializes a new instance of the class. + /// This constructor sets up the necessary services for processing authorization requests, + /// including user authentication, consent handling, authorization code generation, access + /// and identity token services, and time-related functionality. + /// + /// Service for handling user authentication. + /// Service for managing user consent. + /// Service for generating and managing authorization codes. + /// Service for creating access tokens. + /// Service for generating identity tokens. + /// Service for managing time-related operations. + public AuthorizationRequestProcessor( + IAuthSessionService authSessionService, + IConsentService consentService, + IAuthorizationCodeService authorizationCodeService, + IAccessTokenService accessTokenService, + IIdentityTokenService identityTokenService, + TimeProvider clock) + { + _authSessionService = authSessionService; + _consentService = consentService; + _authorizationCodeService = authorizationCodeService; + _accessTokenService = accessTokenService; + _identityTokenService = identityTokenService; + _clock = clock; + } + + private readonly IAccessTokenService _accessTokenService; + private readonly IAuthorizationCodeService _authorizationCodeService; + private readonly IAuthSessionService _authSessionService; + private readonly IConsentService _consentService; + private readonly IIdentityTokenService _identityTokenService; + private readonly TimeProvider _clock; + + /// + /// Asynchronously processes a valid authorization request. + /// This method orchestrates the flow of handling an authorization request, including user authentication, + /// consent validation, and token generation. It determines the appropriate response based on the request's + /// parameters and the user's session state, which may include prompting for login, consent or directly generating + /// authorization codes and tokens. + /// + /// The valid authorization request to process. + /// + /// An representing the outcome of the processed authorization request. + /// This response could be a successful authentication, an error, or a requirement for further user interaction + /// (like login or consent). + /// + public async Task ProcessAsync(ValidAuthorizationRequest request) + { + request.ClientInfo.CheckClient(); + var model = request.Model; + + var authSessions = await GetAvailableAuthSessionsAsync(model); + + if (authSessions.Count == 0 || model.Prompt == Prompts.Login) + { + if (model.Prompt == Prompts.None) + { + return new AuthorizationError( + model, + ErrorCodes.LoginRequired, + "The Authorization Server requires End-User authentication.", + request.ResponseMode, + model.RedirectUri); + } + + return new LoginRequired(model); + } + + if (authSessions.Count > 1 || model.Prompt == Prompts.SelectAccount) + { + if (model.Prompt == Prompts.None) + { + return new AuthorizationError( + model, + ErrorCodes.AccountSelectionRequired, + "The End-User is to select a session at the Authorization Server.", + request.ResponseMode, + model.RedirectUri); + } + + return new AccountSelectionRequired(model, authSessions.ToArray()); + } + + var authSession = authSessions.Single(); + + if (model.Prompt == Prompts.Consent || await _consentService.IsConsentRequired(request, authSession)) + { + if (model.Prompt == Prompts.None) + { + return new AuthorizationError( + model, + ErrorCodes.ConsentRequired, + "The Authorization Server requires End-User consent.", + request.ResponseMode, + model.RedirectUri); + } + + return new ConsentRequired(model, authSession); + } + + var clientId = request.ClientInfo.ClientId; + var authContext = new AuthorizationContext(clientId, model.Scope, model.Claims) + { + RedirectUri = model.RedirectUri, + Nonce = model.Nonce, + CodeChallenge = model.CodeChallenge, + CodeChallengeMethod = model.CodeChallengeMethod, + }; + + if (!authSession.AffectedClientIds.Contains(clientId)) + { + authSession.AffectedClientIds.Add(clientId); + await _authSessionService.SignInAsync(authSession); + } + + var result = new SuccessfullyAuthenticated( + model, + request.ResponseMode, + authSession.SessionId, + authSession.AffectedClientIds); + + var codeRequired = request.Model.ResponseType.HasFlag(ResponseTypes.Code); + if (codeRequired) + { + result.Code = await _authorizationCodeService.GenerateAuthorizationCodeAsync( + new AuthorizedGrant(authSession, authContext), + request.ClientInfo.AuthorizationCodeExpiresIn); + } + + var tokenRequired = request.Model.ResponseType.HasFlag(ResponseTypes.Token); + if (tokenRequired) + { + result.TokenType = TokenTypes.Bearer; + + var accessToken = await _accessTokenService.CreateAccessTokenAsync( + authSession, + authContext, + request.ClientInfo); + + result.AccessToken = accessToken; + } + + var idTokenRequired = request.Model.ResponseType.HasFlag(ResponseTypes.IdToken); + if (idTokenRequired) + { + var idToken = await _identityTokenService.CreateIdentityTokenAsync( + authSession, + authContext, + request.ClientInfo, + !codeRequired && !tokenRequired, + result.Code, + result.AccessToken?.EncodedJwt); + + result.IdToken = idToken; + } + + return result; + } + + private Task> GetAvailableAuthSessionsAsync(AuthorizationRequest model) + { + var authSessions = _authSessionService.GetAvailableAuthSessions(); + + if (model.MaxAge.HasValue) + { + // skip all sessions older than max_age value + var minAuthenticationTime = _clock.GetUtcNow() - model.MaxAge; + authSessions = authSessions + .WhereAsync(session => minAuthenticationTime < session.AuthenticationTime); + } + + var acrValues = model.AcrValues; + if (acrValues is { Length: > 0 }) + { + authSessions = authSessions + .WhereAsync(session => session.AuthContextClassRef.HasValue() && + acrValues.Contains(session.AuthContextClassRef)); + } + + return authSessions.ToListAsync(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestValidator.cs new file mode 100644 index 00000000..a5e3a7a7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/AuthorizationRequestValidator.cs @@ -0,0 +1,71 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization; + +/// +/// Implements the Chain of Responsibility design pattern for processing authorization requests. +/// This class delegates the validation of authorization requests to an , +/// allowing a sequence of validators to handle the request in a decoupled manner. Each validator in the chain +/// processes the request and potentially passes it along to the next validator. +/// +public class AuthorizationRequestValidator : IAuthorizationRequestValidator +{ + /// + /// Initializes a new instance of the class, + /// setting up a chain of responsibility with the provided authorization context validator. + /// This approach enables flexible and modular handling of validation logic. + /// + /// The first validator in the chain to handle the authorization context. + public AuthorizationRequestValidator(IAuthorizationContextValidator validator) + { + _validator = validator; + } + + private readonly IAuthorizationContextValidator _validator; + + /// + /// Asynchronously validates an by passing it through a chain of validators. + /// The method creates a validation context and delegates the validation process to the initial validator in the chain, + /// which can then pass the request to subsequent validators as necessary. + /// + /// The authorization request to validate. + /// + /// An representing the outcome of the validation process, + /// which may be the result of processing by one or more validators in the chain. + /// + public async Task ValidateAsync(AuthorizationRequest request) + { + var context = new AuthorizationValidationContext(request); + + var result = await _validator.ValidateAsync(context) ?? + (AuthorizationRequestValidationResult)new ValidAuthorizationRequest(context); + + return result; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AccountSelectionRequired.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AccountSelectionRequired.cs new file mode 100644 index 00000000..936523b6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AccountSelectionRequired.cs @@ -0,0 +1,33 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Means that a user is logged into several accounts currently and it is to select one of them to continue +/// +public record AccountSelectionRequired(AuthorizationRequest Model, AuthSession[] Users) + : AuthorizationResponse(Model); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationEndpointMetadata.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationEndpointMetadata.cs new file mode 100644 index 00000000..7e887e35 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationEndpointMetadata.cs @@ -0,0 +1,87 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Holds metadata for the authorization endpoint, detailing the capabilities and supported standards. +/// +public record AuthorizationEndpointMetadata +{ + /// + /// Indicates whether the 'request' parameter of an authorization request is supported. + /// This parameter is used for passing a request object by value. + /// + public bool RequestParameterSupported { get; init; } + + /// + /// Indicates whether the claims parameter is supported for requesting specific claims. + /// + public bool ClaimsParameterSupported { get; init; } + + /// + /// The response types the authorization server supports. + /// + public List ResponseTypesSupported { get; init; } = new() + { + ResponseTypes.Code, + ResponseTypes.Token, + ResponseTypes.IdToken, + string.Join(' ', ResponseTypes.Code, ResponseTypes.Token), + string.Join(' ', ResponseTypes.Code, ResponseTypes.IdToken), + string.Join(' ', ResponseTypes.Token, ResponseTypes.IdToken), + string.Join(' ', ResponseTypes.Code, ResponseTypes.Token, ResponseTypes.IdToken), + }; + + /// + /// The response modes the authorization server supports for returning parameters from the authorization endpoint. + /// + public List ResponseModesSupported { get; init; } = new() + { + ResponseModes.Query, + ResponseModes.Fragment, + ResponseModes.FormPost, + }; + + /// + /// The prompt values the authorization server supports for interaction with the end-user. + /// + public List PromptValuesSupported { get; init; } = new() + { + Prompts.SelectAccount, + Prompts.Consent, + Prompts.None, + Prompts.Login, + Prompts.Create, + }; + + /// + /// The code challenge methods supported for PKCE (Proof Key for Code Exchange). + /// + public List CodeChallengeMethodsSupported { get; init; } = new() + { + CodeChallengeMethods.S256, + CodeChallengeMethods.Plain, + }; +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationError.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationError.cs new file mode 100644 index 00000000..fb18b331 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationError.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Represents an error response for an authorization request, detailing the nature of the error. +/// +/// The original authorization request model that led to this error. +/// A single error code from the OAuth 2.0 specification that describes the error. +/// A more detailed description of the error for debugging purposes. +/// The response mode to be used for returning parameters to the client. +/// This can influence how the error information is transmitted back to the client. +/// The URI to which the response should be sent. This is where the error information +/// will be transmitted if applicable. +/// A URI identifying a human-readable web page with information about the error. +/// +/// This record encapsulates information about errors encountered during the processing of an authorization request. +/// It includes details that can be returned to the client to indicate what went wrong. This structure facilitates +/// compliance with OAuth 2.0 and OpenID Connect specifications by providing a standardized format for error reporting. +/// +public record AuthorizationError( + AuthorizationRequest Model, + string Error, + string ErrorDescription, + string ResponseMode, + Uri? RedirectUri, + Uri? ErrorUri = null) + : AuthorizationResponse(Model) +{ + /// + /// Constructs an instance of from an and + /// an . + /// + /// The request that resulted in the error. + /// The validation error that provides details about what caused the request to fail. + public AuthorizationError(AuthorizationRequest request, AuthorizationRequestValidationError error) + : this(request, + error.Error, + error.ErrorDescription, + error.ResponseMode, + error.RedirectUri) + { + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationError.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationError.cs new file mode 100644 index 00000000..4e02af9a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationError.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Represents a specific type of validation result for an authorization request that has been deemed invalid. +/// This record details the nature of the validation failure through an error code and description, providing +/// insights into why the request did not pass validation checks. It also includes the redirect URI to guide +/// the client on where to direct the user for further actions if necessary. +/// +public record AuthorizationRequestValidationError(string Error, string ErrorDescription, Uri? RedirectUri, string ResponseMode) + : AuthorizationRequestValidationResult(ResponseMode); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationResult.cs new file mode 100644 index 00000000..e145caab --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationRequestValidationResult.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Serves as the base record for representing the result of an authorization request validation process. +/// It encapsulates the response mode that indicates how the authorization response should be delivered to the client, +/// providing a foundation for specific types of validation results, such as successful validations, errors +/// or other custom outcomes. +/// +/// +/// The response mode to be used for delivering the authorization response, indicating whether the response +/// should be returned directly, via a redirection URI, or using other methods as defined +/// by the OAuth 2.0 specification and extensions. +/// +public abstract record AuthorizationRequestValidationResult(string ResponseMode = ResponseModes.Query); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationResponse.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationResponse.cs new file mode 100644 index 00000000..81c173ba --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/AuthorizationResponse.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Represents the response to an authorization request. +/// This abstract record serves as a base for specific authorization response +/// implementations. It should be inherited by classes that define the detailed +/// structure and behavior of different types of authorization responses. +/// +public abstract record AuthorizationResponse(AuthorizationRequest Model) +{ + /// + /// The original or recovered request model that was processed. + /// + public AuthorizationRequest Model { get; init; } = Model; +}; diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ConsentRequired.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ConsentRequired.cs new file mode 100644 index 00000000..6b7331dd --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ConsentRequired.cs @@ -0,0 +1,33 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Means that a user is logged and it is to ask for his consent for authorization. +/// +public record ConsentRequired(AuthorizationRequest Model, AuthSession AuthSession) + : AuthorizationResponse(Model); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationHandler.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationHandler.cs new file mode 100644 index 00000000..b4ef7e8c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationHandler.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Defines a contract for handling authorization requests, ensuring they are processed according to OAuth 2.0 +/// and OpenID Connect protocol specifications. +/// +public interface IAuthorizationHandler +{ + /// + /// Metadata related to the authorization endpoint, detailing supported features such as claims parameters, + /// response types, response modes, prompt values, and code challenge methods. + /// + AuthorizationEndpointMetadata Metadata { get; } + + /// + /// Asynchronously processes an authorization request, validating its parameters and generating an appropriate + /// response that either grants or denies the authorization based on the application's logic and security requirements. + /// + /// The authorization request containing necessary information for processing, + /// such as client ID, requested scopes, redirect URI, and other protocol-specific parameters. + /// A task that results in an , encapsulating either a successful + /// authorization with tokens and additional data or an error response indicating why the authorization failed. + /// + /// Implementations of this interface are responsible for the core logic associated with the OAuth 2.0 and OpenID Connect + /// authorization process, including but not limited to, validating request integrity, authenticating the user, + /// obtaining user consent and issuing authorization codes or tokens. + /// This method is central to the authorization endpoint's functionality. + /// + Task HandleAsync(Model.AuthorizationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestProcessor.cs new file mode 100644 index 00000000..b372ec6c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestProcessor.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Defines the interface for processing authorization requests according to OpenID Connect and OAuth 2.0 specifications. +/// It handles the end-user's authentication, authorization decision, and the issuance of authorization codes and tokens. +/// +/// +/// The actual authentication methods and the process to obtain the end-user's authorization decision are +/// implementation-specific and not defined by this interface. +/// +public interface IAuthorizationRequestProcessor +{ + /// + /// Processes a valid authorization request, authenticates the end-user, obtains an authorization decision, + /// and issues an authorization code or tokens. + /// + /// The valid authorization request to process. + /// A task that resolves to an containing the outcome + /// of the request processing. + Task ProcessAsync(ValidAuthorizationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestValidator.cs new file mode 100644 index 00000000..c86fd3e7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/IAuthorizationRequestValidator.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Defines the interface for validating authorization requests in accordance with OpenID Connect Core 1.0 +/// specifications. It assesses if a request complies with the required parameters and constraints for +/// authentication and authorization processes. +/// +/// +/// For more details on authorization request validation, refer to the OpenID Connect Core 1.0 specification. +/// +public interface IAuthorizationRequestValidator +{ + /// + /// Asynchronously validates an authorization request against the OpenID Connect Core 1.0 specifications, + /// ensuring it meets the required criteria for processing. + /// + /// The authorization request to validate. + /// A task that resolves to a validation result indicating the request's compliance with + /// the specifications. + Task ValidateAsync(AuthorizationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/InteractionRequired.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/InteractionRequired.cs new file mode 100644 index 00000000..9d3a6442 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/InteractionRequired.cs @@ -0,0 +1,33 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Claims; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Means that a current user is logged in, but some additional interaction with him is to continue +/// +public record InteractionRequired(AuthorizationRequest Model, ClaimsPrincipal User) + : AuthorizationResponse(Model); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/LoginRequired.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/LoginRequired.cs new file mode 100644 index 00000000..0fea52f8 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/LoginRequired.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Means that there is no user logged in or the current user is forced to re-authenticate by the client +/// +public record LoginRequired(AuthorizationRequest Model) + : AuthorizationResponse(Model); diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/SuccessfullyAuthenticated.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/SuccessfullyAuthenticated.cs new file mode 100644 index 00000000..a0f98231 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/SuccessfullyAuthenticated.cs @@ -0,0 +1,69 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Represents a successful authentication response, encapsulating details about the outcome +/// of an authentication request, including any tokens issued as a result. +/// +/// The original authorization request that led to this successful authentication. +/// Specifies how the result of the authentication should be returned to the client. +/// An optional session identifier that may be used for session management. +/// Identifiers of the clients that are affected by or related to this authentication +/// process. +public record SuccessfullyAuthenticated(AuthorizationRequest Model, string ResponseMode, string? SessionId, ICollection AffectedClientIds) + : AuthorizationResponse(Model) +{ + /// + /// An authorization code that can be exchanged for tokens. This code is issued only if + /// the authentication request was successful and the response type requested an authorization code. + /// + public string? Code { get; set; } + + /// + /// The type of token issued, typically "Bearer", indicating how the issued token may be used. + /// This property is populated if an access token is issued as part of the authentication response. + /// + public string? TokenType { get; set; } + + /// + /// The access token issued as part of the authentication response, encoded in a format suitable for transmission. + /// Access tokens are credentials used to access protected resources. + /// + public EncodedJsonWebToken? AccessToken { get; set; } + + /// + /// The ID token issued as part of the authentication response, providing identity information about the user. + /// Encoded in a format suitable for transmission. + /// + public EncodedJsonWebToken? IdToken { get; set; } + + /// + /// An optional state parameter reflecting the session state. This can be used to represent the state of the user's + /// session at the authorization server and may be used for managing session continuity and logout. + /// + public string? SessionState { get; set; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ValidAuthorizationRequest.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ValidAuthorizationRequest.cs new file mode 100644 index 00000000..a4e7ff40 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Interfaces/ValidAuthorizationRequest.cs @@ -0,0 +1,53 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +/// +/// Represents an authorization request that has passed validation, encapsulating all necessary information +/// resolved during its validation. +/// +public record ValidAuthorizationRequest : AuthorizationRequestValidationResult +{ + public ValidAuthorizationRequest(AuthorizationValidationContext context) + : base(context.ResponseMode) + { + Model = context.Request; + ClientInfo = context.ClientInfo; + } + + /// + /// The original or recovered request model that was validated. + /// + public AuthorizationRequest Model { get; init; } + + /// + /// Information about the client making the request, as determined during validation. + /// + public ClientInfo ClientInfo { get; init; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/CompositeRequestFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/CompositeRequestFetcher.cs new file mode 100644 index 00000000..0823007b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/CompositeRequestFetcher.cs @@ -0,0 +1,58 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +public class CompositeRequestFetcher : IAuthorizationRequestFetcher +{ + public CompositeRequestFetcher(IAuthorizationRequestFetcher[] fetchers) + { + _fetchers = fetchers; + } + + private readonly IAuthorizationRequestFetcher[] _fetchers; + + public async Task FetchAsync(AuthorizationRequest request) + { + foreach (var fetcher in _fetchers) + { + var result = await fetcher.FetchAsync(request); + switch (result) + { + case FetchResult.Success success: + request = success.Request; + continue; + + case FetchResult.Fault fault: + return fault; + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + } + + return request; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/FetchResult.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/FetchResult.cs new file mode 100644 index 00000000..4b0eabab --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/FetchResult.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +/// +/// Represents the result of fetching an authorization request, either successful with the request data or +/// indicating a fault with an associated error. +/// +public abstract record FetchResult +{ + /// + /// Converts an to a indicating a successful + /// fetch operation. + /// + /// The successfully fetched authorization request. + public static implicit operator FetchResult(AuthorizationRequest request) + => new Success(request); + + /// + /// Converts an to a indicating + /// a fault during fetch operation. + /// + /// The error occurred while fetching the authorization request. + public static implicit operator FetchResult(AuthorizationRequestValidationError error) + => new Fault(error); + + /// + /// Represents a successful fetch result containing the authorization request. + /// + public record Success(AuthorizationRequest Request) : FetchResult; + + /// + /// Represents a fault in fetching the authorization request, encapsulating the associated error. + /// + public record Fault(AuthorizationRequestValidationError Error) : FetchResult; +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/IAuthorizationRequestFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/IAuthorizationRequestFetcher.cs new file mode 100644 index 00000000..45250bf8 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/IAuthorizationRequestFetcher.cs @@ -0,0 +1,50 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +/// +/// Defines an interface for fetching the details of an authorization request, potentially including resolving and +/// validating a request object. +/// +public interface IAuthorizationRequestFetcher +{ + /// + /// Asynchronously fetches and processes the authorization request, which may involve resolving a request object + /// from a URI or directly from the request parameters. + /// + /// + /// The initial authorization request, which may contain a reference to a request object or inline request parameters. + /// + /// + /// A task that represents the asynchronous operation. The task result contains the processed authorization request + /// details, encapsulated within a . + /// + /// This method is responsible for handling the specifics of fetching and interpreting the authorization request, + /// which may include retrieving a request object from a remote location specified by the 'request_uri' parameter, + /// or validating the request object provided inline via the 'request' parameter. It ensures the request adheres + /// to the expected format and validation requirements before further processing. + /// + Task FetchAsync(AuthorizationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/PushedRequestFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/PushedRequestFetcher.cs new file mode 100644 index 00000000..47724d00 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/PushedRequestFetcher.cs @@ -0,0 +1,77 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +/// +/// Fetches pushed authorization request objects identified by a URN (Uniform Resource Name) from a storage system. +/// +public class PushedRequestFetcher : IAuthorizationRequestFetcher +{ + /// + /// Initializes a new instance of the class. + /// + /// The storage system used to retrieve pushed authorization + /// request objects. + public PushedRequestFetcher(IAuthorizationRequestStorage authorizationRequestStorage) + { + _authorizationRequestStorage = authorizationRequestStorage; + } + + private readonly IAuthorizationRequestStorage _authorizationRequestStorage; + + /// + /// Asynchronously retrieves the pushed authorization request object associated with the specified URN. + /// + /// + /// The authorization request containing a URN from which to fetch the stored pushed authorization request object. + /// + /// + /// A task representing the asynchronous operation. The task result contains the fetched pushed authorization + /// request object or an error if not found. + /// + /// This method checks if the provided authorization request contains a URN that references a pushed authorization + /// request stored in the system. If the URN is valid and corresponds to a stored request, the method retrieves + /// and returns the request object. If the request object cannot be found or the URN is invalid, + /// an error is returned. + /// + public async Task FetchAsync(AuthorizationRequest request) + { + if (request is { RequestUri: { } requestUrn } && + requestUrn.OriginalString.StartsWith(RequestUrn.Prefix)) + { + var requestObject = await _authorizationRequestStorage.TryGetAsync(requestUrn, true); + return requestObject switch + { + null => ErrorFactory.InvalidRequestUri($"Can't find a request by {requestUrn}"), + _ => requestObject, + }; + } + + return request; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs new file mode 100644 index 00000000..b81ada18 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs @@ -0,0 +1,126 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +/// +/// Implements the fetching and processing of authorization request objects, including JWT validation and model binding. +/// +public class RequestObjectFetcher : IAuthorizationRequestFetcher +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger for logging debug and warning messages. + /// The validator for JSON Web Tokens (JWTs). + /// The binder for converting JSON payloads into AuthorizationRequest objects. + /// The provider for client JSON Web Key Sets (JWKS). + /// The provider for client information. + public RequestObjectFetcher( + ILogger logger, + IJsonWebTokenValidator jwtValidator, + IJsonObjectBinder jsonObjectBinder, + IClientKeysProvider clientJwksProvider, + IClientInfoProvider clientInfoProvider) + { + _logger = logger; + _jwtValidator = jwtValidator; + _jsonObjectBinder = jsonObjectBinder; + _clientJwksProvider = clientJwksProvider; + _clientInfoProvider = clientInfoProvider; + } + + private readonly ILogger _logger; + private readonly IJsonWebTokenValidator _jwtValidator; + private readonly IJsonObjectBinder _jsonObjectBinder; + private readonly IClientKeysProvider _clientJwksProvider; + private readonly IClientInfoProvider _clientInfoProvider; + + + /// + /// Fetches and processes the authorization request object, validating its JWT and binding its contents to a new + /// or updated AuthorizationRequest. + /// + /// + /// The initial authorization request, potentially containing a 'request' parameter with the JWT. + /// + /// A task that represents the asynchronous operation. The task result contains the processed authorization + /// request or an error. + /// + /// This method decodes and validates the JWT included in the 'request' parameter of the authorization request. + /// If valid, it binds the JWT payload to the authorization request model. If the JWT is invalid, it logs + /// a warning and returns an error. + /// + public async Task FetchAsync(AuthorizationRequest request) + { + if (request is { Request: { } requestObject }) + { + _logger.LogDebug("JWT request object was: {RequestObject}", requestObject); + + var result = await _jwtValidator.ValidateAsync( + requestObject, + new ValidationParameters + { + Options = ValidationOptions.ValidateIssuerSigningKey, + ResolveIssuerSigningKeys = ResolveIssuerSigningKeys, + }); + + switch (result) + { + case ValidJsonWebToken { Token.Payload.Json: var json }: + var updatedRequest = await _jsonObjectBinder.BindModelAsync(json, request); + if (updatedRequest == null) + return ErrorFactory.InvalidRequestObject($"Unable to bind request object"); + + return updatedRequest; + + case JwtValidationError error: + _logger.LogWarning("The request object contains invalid token: {@Error}", error); + return ErrorFactory.InvalidRequestObject($"The request object is invalid."); + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + } + + return request; + } + + private async IAsyncEnumerable ResolveIssuerSigningKeys(string clientId) + { + var clientInfo = await _clientInfoProvider.TryFindClientAsync(clientId).WithLicenseCheck(); + if (clientInfo == null) + yield break; + + await foreach (var key in _clientJwksProvider.GetSigningKeys(clientInfo)) + yield return key; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestUriFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestUriFetcher.cs new file mode 100644 index 00000000..869d4726 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestUriFetcher.cs @@ -0,0 +1,95 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; +using static Abblix.Oidc.Server.Model.AuthorizationRequest; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; + +/// +/// Fetches authorization request objects from a specified request URI. +/// +public class RequestUriFetcher : IAuthorizationRequestFetcher +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger used for logging warnings when unable to fetch the request object. + /// + /// The factory used to create instances of for making HTTP requests. + public RequestUriFetcher( + ILogger logger, + IHttpClientFactory httpClientFactory) + { + _logger = logger; + _httpClientFactory = httpClientFactory; + } + + private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + + + /// + /// Asynchronously fetches the authorization request object from the specified request URI. + /// + /// + /// The authorization request containing a RequestUri from which to fetch the request object. + /// + /// A task representing the asynchronous operation. The task result contains the authorization request object + /// fetched from the URI or an error. + /// + /// If the authorization request contains a valid absolute URI in the RequestUri property, + /// this method attempts to fetch the request object from that URI. + /// If the fetch operation is successful, the request object is returned; otherwise, an error is logged, + /// and an error response is returned. + /// + public async Task FetchAsync(AuthorizationRequest request) + { + if (request is { Request: not null, RequestUri: not null }) + { + return ErrorFactory.InvalidRequest( + $"Only one of the parameters {Parameters.Request} and {Parameters.RequestUri} can be used"); + } + + if (request is { RequestUri: { IsAbsoluteUri: true } requestUri }) + { + var client = _httpClientFactory.CreateClient(); + string requestObject; + try + { + requestObject = await client.GetStringAsync(requestUri); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unable to get the request object from {RequestUri}", requestUri); + return ErrorFactory.InvalidRequestUri( + $"Unable to get the request object from {Parameters.RequestUri}"); + } + + return request with { RedirectUri = null, Request = requestObject }; + } + + return request; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationContextValidatorComposite.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationContextValidatorComposite.cs new file mode 100644 index 00000000..9b83f840 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationContextValidatorComposite.cs @@ -0,0 +1,69 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Represents a composite validator for authorization contexts. +/// This class implements and aggregates multiple +/// validation steps into a single validation process. +/// +public class AuthorizationContextValidatorComposite : IAuthorizationContextValidator +{ + /// + /// Initializes a new instance of the class + /// with a set of validation steps. + /// + /// An array of validators that define the validation process. + public AuthorizationContextValidatorComposite(IAuthorizationContextValidator[] validators) + { + _validators = validators; + } + + /// + /// The array of validators representing the steps in the validation process. + /// + private readonly IAuthorizationContextValidator[] _validators; + + /// + /// Asynchronously validates an . + /// Iterates through each validation step, returning the first encountered error, if any. + /// + /// The authorization validation context to be validated. + /// + /// A task that represents the asynchronous validation operation. The task result contains + /// an if a validation error is found, or null if validation succeeds. + /// + public async Task ValidateAsync(AuthorizationValidationContext context) + { + foreach (var validator in _validators) + { + var error = await validator.ValidateAsync(context); + if (error != null) + return error; + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationValidationContext.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationValidationContext.cs new file mode 100644 index 00000000..59123716 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/AuthorizationValidationContext.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Represents a validation context containing information about the client, response mode, and flow type +/// that is used during the authorization request validation process. +/// +public record AuthorizationValidationContext(AuthorizationRequest Request) +{ + /// + /// The request object to validate. + /// + public AuthorizationRequest Request { get; set; } = Request; + + private ClientInfo? _clientInfo; + + /// + /// The ClientInfo object containing information about the client. It is a result of identifying the client + /// making the authorization request. + /// + /// Thrown when attempting to get a null value. + public ClientInfo ClientInfo { get => _clientInfo.NotNull(nameof(ClientInfo)); set => _clientInfo = value; } + + /// + /// The response mode associated with the authorization request, determining how the authorization response + /// should be delivered to the client. + /// + public string ResponseMode = ResponseModes.Query; + + private FlowTypes? _flowType; + + /// + /// The flow type associated with the authorization request, indicating the OAuth 2.0 flow being utilized + /// (e.g., Authorization Code, Implicit). + /// + /// Thrown when attempting to get a null value. + public FlowTypes FlowType { get => _flowType.NotNull(nameof(FlowType)); set => _flowType = value; } + + /// + /// The validated and approved redirect URI for the authorization response. + /// This URI must match one of the URIs registered by the client. + /// + public Uri? ValidRedirectUri { get; set; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ClientValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ClientValidator.cs new file mode 100644 index 00000000..85ae0af4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ClientValidator.cs @@ -0,0 +1,83 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Validates the client specified in the authorization request. +/// This class checks whether the client is registered and authorized to perform the request, +/// as part of the authorization validation process. It plays a crucial role in ensuring that +/// only valid and authorized clients can initiate authorization requests. +/// +public class ClientValidator : IAuthorizationContextValidator +{ + /// + /// Initializes a new instance of the class with dependencies for client information + /// retrieval and logging. The client info provider is used to obtain detailed information about clients, + /// while the logger records validation activities. + /// + /// The provider used to retrieve information about clients. + /// The logger to be used for recording validation activities and outcomes. + public ClientValidator( + IClientInfoProvider clientInfoProvider, + ILogger logger) + { + _clientInfoProvider = clientInfoProvider; + _logger = logger; + } + + private readonly IClientInfoProvider _clientInfoProvider; + private readonly ILogger _logger; + + /// + /// Asynchronously validates the client specified in the authorization request. + /// Ensures the client is recognized and authorized to make the request. + /// + /// + /// The validation context containing details of the authorization request and client information. + /// + /// + /// An if the client is not found or not authorized, + /// or null if the client is valid. + /// + public async Task ValidateAsync(AuthorizationValidationContext context) + { + var clientId = context.Request.ClientId; + var clientInfo = await _clientInfoProvider.TryFindClientAsync(clientId.NotNull(nameof(clientId))).WithLicenseCheck(); + if (clientInfo == null) + { + _logger.LogWarning("The client with id {ClientId} was not found", clientId); + return context.InvalidRequest("The client is not authorized"); + } + + context.ClientInfo = clientInfo; + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ErrorFactory.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ErrorFactory.cs new file mode 100644 index 00000000..96a73711 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ErrorFactory.cs @@ -0,0 +1,122 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Provides a factory for creating standardized authorization request error responses. +/// This factory simplifies the creation of error responses for various types of validation failures +/// during authorization request processing. +/// +public static class ErrorFactory +{ + /// + /// Creates an indicating an invalid request. + /// This error type is commonly used when an authorization request fails due to missing or invalid parameters. + /// + /// The validation context associated with the request, providing additional context for + /// the error response. + /// A human-readable explanation detailing what was invalid about the request. + /// + /// An instance encapsulating the error details. + public static AuthorizationRequestValidationError InvalidRequest( + this AuthorizationValidationContext context, + string description) + { + return context.Error(ErrorCodes.InvalidRequest, description); + } + + /// + /// Creates an indicating an invalid client error. + /// This error type is used when the client authentication fails or when the client is not authorized to perform + /// the requested operation. It may occur due to issues like incorrect client credentials, unauthorized grant types + /// for the client, or the client being unknown to the authorization server. + /// + /// A human-readable description specifying why the client is considered invalid. + /// An with the specified error details, indicating that + /// the error relates to an invalid client. + public static AuthorizationRequestValidationError InvalidClient(string description) + => ValidationError(ErrorCodes.InvalidClient, description); + + /// + /// Creates an for a general invalid request error, + /// without an associated validation context. + /// + /// A description of what was invalid about the request. + /// An with the specified error details. + public static AuthorizationRequestValidationError InvalidRequest(string description) + => ValidationError(ErrorCodes.InvalidRequest, description); + + /// + /// Creates an indicating an invalid request URI. + /// This error is used when the request_uri parameter of an authorization request is invalid or malformed. + /// + /// A description of the issue with the request URI. + /// An for the invalid request URI. + public static AuthorizationRequestValidationError InvalidRequestUri(string description) + => ValidationError(ErrorCodes.InvalidRequestUri, description); + + /// + /// Creates an indicating an invalid request object. + /// This error is used when the request object (JWT) in an authorization request is invalid, such as when + /// signature validation fails or required claims are missing. + /// + /// A description of the issue with the request object. + /// An for the invalid request object. + public static AuthorizationRequestValidationError InvalidRequestObject(string description) + => ValidationError(ErrorCodes.InvalidRequestObject, description); + + /// + /// A private helper method to create an with + /// a specified error code and description. + /// + /// The error code as defined by the OpenID Connect and OAuth 2.0 specifications. + /// A human-readable description of the error. + /// + /// An instance with the specified error details. + private static AuthorizationRequestValidationError ValidationError(string error, string description) => new( + error, + description, + null, + string.Empty); + + /// + /// Creates an with a specified error code and description, + /// using the context from an . + /// + /// The validation context associated with the authorization request. + /// The error code as defined by the OpenID Connect and OAuth 2.0 specifications. + /// A human-readable description of the error. + /// + /// An instance encapsulating the error details. + public static AuthorizationRequestValidationError Error( + this AuthorizationValidationContext context, + string error, + string description) => new( + error, + description, + context.ValidRedirectUri, + context.ResponseMode); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/FlowTypeValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/FlowTypeValidator.cs new file mode 100644 index 00000000..38e3fdc3 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/FlowTypeValidator.cs @@ -0,0 +1,103 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics.CodeAnalysis; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +using Microsoft.Extensions.Logging; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Validates the OAuth 2.0 flow type specified in the authorization request. +/// This class determines if the requested flow type is supported and matches the +/// expected patterns for authorization requests, as part of the validation process. +/// +public class FlowTypeValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Initializes a new instance of the class with a logger. + /// The logger is used for recording the validation activities, aiding in troubleshooting and auditing. + /// + /// The logger to be used for logging purposes. + public FlowTypeValidator(ILogger logger) + { + _logger = logger; + } + + private readonly ILogger _logger; + + /// + /// Validates the flow type specified in the authorization request. + /// This method checks if the flow type is supported and aligns with the OAuth 2.0 specifications. + /// + /// The validation context containing client and request information. + /// + /// An if the flow type is not valid or supported, + /// or null if the flow type is valid. + /// + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + var responseType = context.Request.ResponseType; + if (!TryDetectFlowType(responseType, out var flowType, out var responseMode)) + { + _logger.LogWarning("The response type {@ResponseType} is not valid", new object?[] { responseType }); + + context.ResponseMode = context.Request.ResponseMode ?? ResponseModes.Query; + + return context.Error( + ErrorCodes.UnsupportedResponseType, + "The response type is not supported"); + } + + context.FlowType = flowType; + context.ResponseMode = responseMode; + return null; + } + + /// + /// Attempts to detect the OAuth 2.0 flow type based on the specified response types. + /// + /// An array of response types to examine. + /// The detected flow type, if successful. + /// The default response mode for the detected flow type, if successful. + /// A boolean value indicating whether the detection was successful. + private static bool TryDetectFlowType([NotNullWhen(true)] string[]? responseType, out FlowTypes flowType, out string responseMode) + { + var code = responseType.HasFlag(ResponseTypes.Code); + var token = responseType.HasFlag(ResponseTypes.Token) || responseType.HasFlag(ResponseTypes.IdToken); + + (var result, flowType, responseMode) = (code, token) switch + { + (code: true, token: false) => (true, FlowTypes.AuthorizationCode, ResponseModes.Query), + (code: false, token: true) => (true, FlowTypes.Implicit, ResponseModes.Fragment), + (code: true, token: true) => (true, FlowTypes.Hybrid, ResponseModes.Fragment), + _ => (false, default, default!), + }; + + return result; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/IAuthorizationContextValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/IAuthorizationContextValidator.cs new file mode 100644 index 00000000..ab5181ac --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/IAuthorizationContextValidator.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Defines the contract for a validator in an authorization context. +/// Implementations of this interface should provide logic for validating +/// authorization requests. +/// +public interface IAuthorizationContextValidator +{ + /// + /// Asynchronously validates an authorization request within a given context. + /// + /// + /// The that contains the details + /// of the authorization request to be validated. + /// + /// + /// A task that represents the asynchronous validation operation. The task result contains + /// an if a validation error is found, + /// or null if validation is successful. + /// + Task ValidateAsync(AuthorizationValidationContext context); +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/NonceValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/NonceValidator.cs new file mode 100644 index 00000000..000a6b04 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/NonceValidator.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Utils; +using static Abblix.Oidc.Server.Model.AuthorizationRequest; + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Implements nonce validation for authorization requests in compliance with OAuth 2.0 and OpenID Connect specifications. +/// This validator ensures the presence of a nonce parameter when the response type includes an ID token, as by +/// OpenID Connect Core 1.0 specification. It extends for +/// synchronous validation. +/// Refer to RFC 6749 and OpenID Connect Core 1.0 for more details on authorization request parameters. +/// +public class NonceValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Validates the nonce in the authorization request as per OpenID Connect Core 1.0 specifications. + /// + /// The containing the authorization request + /// to be validated. + /// + /// An if the validation fails due to a missing nonce + /// when the response type includes an ID token, as by OpenID Connect Core 1.0; + /// otherwise, null indicating successful validation. + /// + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + var request = context.Request; + var responseType = request.ResponseType; + + // Validate nonce as per OpenID Connect Core 1.0 requirements + if (responseType.NotNull(nameof(responseType)).Contains(ResponseTypes.IdToken) && + string.IsNullOrEmpty(request.Nonce)) + { + return context.InvalidRequest($"Nonce is when {Parameters.ResponseType} includes '{ResponseTypes.IdToken}', as specified in OpenID Connect Core 1.0."); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/PkceValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/PkceValidator.cs new file mode 100644 index 00000000..03224c39 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/PkceValidator.cs @@ -0,0 +1,65 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Utils; + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Validates the PKCE (Proof Key for Code Exchange) parameters in an authorization request. +/// PKCE adds an additional layer of security for the OAuth 2.0 authorization code flow, +/// particularly in public clients. It ensures that the authorization request conforms to +/// the standards defined in RFC 7636 (specifically, see Section 4.3 for client validation requirements). +/// +public class PkceValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Validates the PKCE-related parameters in the authorization request against the client's + /// configuration. This method checks for compliance with PKCE specifications as outlined in RFC 7636, + /// with particular attention to the guidelines in Section 4.3 of the document. + /// + /// The validation context containing client information and request details. + /// + /// An AuthorizationRequestValidationError if the validation fails due to non-compliance with PKCE requirements, + /// or null if the request is valid. Refer to Section 4.3 of RFC 7636 for more details. + /// + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + if (context.Request.CodeChallenge.HasValue()) + { + if (context.Request.CodeChallengeMethod == CodeChallengeMethods.Plain && + !context.ClientInfo.PlainPkceAllowed) + { + return context.InvalidRequest("The client is not allowed PKCE plain method"); + } + } + else if (context.ClientInfo.PkceRequired) + { + return context.InvalidRequest("The client requires PKCE code challenge"); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/RedirectUriValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/RedirectUriValidator.cs new file mode 100644 index 00000000..fc88d662 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/RedirectUriValidator.cs @@ -0,0 +1,80 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.UriValidation; +using Microsoft.Extensions.Logging; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Validates the redirect URI specified in the authorization request. +/// This class checks whether the provided redirect URI matches one of the client's registered redirect URIs, +/// as part of the authorization validation process. It is essential for ensuring that redirections +/// only occur to pre-approved locations, enhancing security in the OAuth 2.0 flow. +/// +public class RedirectUriValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Initializes a new instance of the class with a logger. + /// The logger is used to record validation activities and outcomes, providing insights into + /// the validation process and aiding in debugging and audit trails. + /// + /// The logger to be used for logging validation process and outcomes. + public RedirectUriValidator(ILogger logger) + { + _logger = logger; + } + + private readonly ILogger _logger; + + /// + /// Validates the redirect URI specified in the authorization request against the registered redirect URIs + /// for the client. Ensures that the redirect URI is one of the pre-approved URIs for the client making the request. + /// This validation is crucial for preventing unauthorized redirections in the OAuth 2.0 authorization flow. + /// + /// The validation context containing client information and the request details. + /// + /// An if the redirect URI is not valid for the specified client, + /// or null if the redirect URI is valid. + /// + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + var uriValidator = UriValidatorFactory.Create(context.ClientInfo.RedirectUris); + + var redirectUri = context.Request.RedirectUri; + if (redirectUri == null || !uriValidator.IsValid(redirectUri)) + { + _logger.LogWarning("The redirect URI {RedirectUri} is invalid for client with id {ClientId}", + redirectUri, + context.ClientInfo.ClientId); + + return context.InvalidRequest("The redirect URI is not valid for specified client"); + } + + context.ValidRedirectUri = redirectUri; + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ResponseModeValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ResponseModeValidator.cs new file mode 100644 index 00000000..147d8069 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ResponseModeValidator.cs @@ -0,0 +1,88 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// This class is responsible for validating the response mode specified in the authorization request +/// as part of the SyncAuthorizationRequestValidationStep process. +/// +public class ResponseModeValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Initializes a new instance of the ResponseModeValidator class with a logger. + /// + /// The logger to be used for logging purposes. + public ResponseModeValidator(ILogger logger) + { + _logger = logger; + } + + private readonly ILogger _logger; + + /// + /// Validates the response mode specified in the authorization request against the allowed + /// response modes for the detected flow type. + /// + /// The validation context containing client information. + /// An AuthorizationRequestValidationError if the validation fails, or null if the request is valid. + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + var responseMode = context.Request.ResponseMode; + if (responseMode.HasValue()) + { + if (!IsResponseModeAllowed(responseMode, context.FlowType)) + { + _logger.LogWarning("The response mode {ResponseMode} is not compatible with response type {ResponseType}", + responseMode, + context.Request.ResponseType); + + return context.InvalidRequest("The response mode is not supported"); + } + + context.ResponseMode = responseMode; + } + + return null; + } + + /// + /// Determines if the specified response mode is allowed for the given flow type. + /// + /// The response mode to validate. + /// The flow type associated with the authorization request. + /// A boolean value indicating whether the response mode is allowed for the specified flow type. + private static bool IsResponseModeAllowed(string responseMode, FlowTypes flowType) => flowType switch + { + FlowTypes.AuthorizationCode => responseMode is ResponseModes.Query or ResponseModes.FormPost or ResponseModes.Fragment, + FlowTypes.Implicit or FlowTypes.Hybrid => responseMode is ResponseModes.FormPost or ResponseModes.Fragment, + _ => throw new UnexpectedTypeException(nameof(flowType), flowType.GetType()), + }; +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ScopeValidator.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ScopeValidator.cs new file mode 100644 index 00000000..8c7acb39 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/ScopeValidator.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Validates the scopes specified in the OAuth 2.0 authorization request. +/// This class checks if the requested scopes are permissible based on the client's +/// configuration and the OAuth flow type in use. It ensures that only allowed scopes +/// are requested, enhancing security and compliance with the defined authorization policies. +/// +public class ScopeValidator : SyncAuthorizationContextValidatorBase +{ + /// + /// Validates the scopes specified in the authorization request. + /// It checks the compatibility of requested scopes with the client's allowed scopes + /// and the OAuth flow type. For instance, it validates if offline access is requested + /// appropriately and if the client is authorized for such access. + /// + /// The validation context containing client information and request details. + /// + /// An if the scope validation fails, + /// or null if the scopes in the request are valid. + /// + protected override AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context) + { + if (context.Request.Scope.HasFlag(Scopes.OfflineAccess)) + { + if (context.FlowType == FlowTypes.Implicit) + return context.InvalidRequest("It is not allowed to request for offline access in implicit flow"); + + if (!context.ClientInfo.OfflineAccessAllowed) + return context.InvalidRequest("This client is not allowed to request for offline access"); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/Validation/SyncAuthorizationContextValidatorBase.cs b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/SyncAuthorizationContextValidatorBase.cs new file mode 100644 index 00000000..354582d6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Authorization/Validation/SyncAuthorizationContextValidatorBase.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + + + +namespace Abblix.Oidc.Server.Endpoints.Authorization.Validation; + +/// +/// Provides a base for implementing synchronous authorization request validation steps. +/// This abstract class allows for creating validators that perform synchronous validation +/// of authorization requests, while adhering to the +/// interface for asynchronous operation. +/// +public abstract class SyncAuthorizationContextValidatorBase : IAuthorizationContextValidator +{ + /// + /// Synchronously validates the authorization request and wraps the result in a task. + /// This method implements the method + /// to allow synchronous validation logic within an asynchronous method signature. + /// + /// The validation context containing client information and request details. + /// + /// A task representing the result of the synchronous validation. The task contains an + /// if the validation fails, or null if the request is valid. + /// + public Task ValidateAsync(AuthorizationValidationContext context) + => Task.FromResult(Validate(context)); + + /// + /// Abstract method for validating the authorization request. + /// Derived classes must implement this method to provide specific validation logic. + /// + /// The validation context containing client information and request details. + /// + /// An if the validation fails, or null if the request is valid. + /// + protected abstract AuthorizationRequestValidationError? Validate(AuthorizationValidationContext context); +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthentication.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthentication.cs new file mode 100644 index 00000000..42b1c787 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthentication.cs @@ -0,0 +1,25 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; + +public record BackChannelAuthentication; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestProcessor.cs new file mode 100644 index 00000000..f325678b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestProcessor.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + + + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; + +public class BackChannelAuthenticationRequestProcessor : IBackChannelAuthenticationRequestProcessor +{ + public BackChannelAuthenticationRequestProcessor( + IBackChannelAuthenticationStorage storage) + { + _storage = storage; + } + + private readonly IBackChannelAuthenticationStorage _storage; + + /// + public async Task ProcessAsync(ValidBackChannelAuthenticationRequest request) + { + var id = await _storage.StoreAsync(new BackChannelAuthentication()); + + return new BackChannelAuthenticationSuccess(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestValidator.cs new file mode 100644 index 00000000..7d8af1df --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationRequestValidator.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; + +public class BackChannelAuthenticationRequestValidator : IBackChannelAuthenticationRequestValidator +{ + public BackChannelAuthenticationRequestValidator( + IClientAuthenticator clientAuthenticator) + { + _clientAuthenticator = clientAuthenticator; + } + + private readonly IClientAuthenticator _clientAuthenticator; + + /// + public async Task ValidateAsync( + BackChannelAuthenticationRequest request, + ClientRequest clientRequest) + { + var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest); + if (clientInfo == null) + { + return new BackChannelAuthenticationValidationError(ErrorCodes.InvalidClient, "The client is not authorized"); + } + + return new ValidBackChannelAuthenticationRequest(request, clientInfo); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationStorage.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationStorage.cs new file mode 100644 index 00000000..92e397fb --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationStorage.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; + +public class BackChannelAuthenticationStorage : IBackChannelAuthenticationStorage +{ + /// + public Task StoreAsync(BackChannelAuthentication backChannelAuthentication) + { + throw new NotImplementedException(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/IBackChannelAuthenticationStorage.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/IBackChannelAuthenticationStorage.cs new file mode 100644 index 00000000..f7227de9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/IBackChannelAuthenticationStorage.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; + +public interface IBackChannelAuthenticationStorage +{ + Task StoreAsync(BackChannelAuthentication backChannelAuthentication); +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationError.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationError.cs new file mode 100644 index 00000000..403fc7a4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationError.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +/// +/// Indicates that a back-channel authentication request failed and describes what was wrong and why. +/// +public record BackChannelAuthenticationError(string Error, string ErrorDescription) + : BackChannelAuthenticationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationResponse.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationResponse.cs new file mode 100644 index 00000000..75d0a136 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationResponse.cs @@ -0,0 +1,25 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +public abstract record BackChannelAuthenticationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationSuccess.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationSuccess.cs new file mode 100644 index 00000000..9f1511b0 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationSuccess.cs @@ -0,0 +1,25 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +public record BackChannelAuthenticationSuccess : BackChannelAuthenticationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationError.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationError.cs new file mode 100644 index 00000000..b78cb792 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationError.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +/// +/// Indicates that a back-channel authentication request is invalid and describes what was wrong and why. +/// +public record BackChannelAuthenticationValidationError(string Error, string ErrorDescription) + : BackChannelAuthenticationValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationResult.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationResult.cs new file mode 100644 index 00000000..b40f492d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/BackChannelAuthenticationValidationResult.cs @@ -0,0 +1,25 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +public abstract record BackChannelAuthenticationValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestProcessor.cs new file mode 100644 index 00000000..611ff06b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestProcessor.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +public interface IBackChannelAuthenticationRequestProcessor +{ + Task ProcessAsync(ValidBackChannelAuthenticationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestValidator.cs new file mode 100644 index 00000000..10b709ef --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/IBackChannelAuthenticationRequestValidator.cs @@ -0,0 +1,53 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +/// +/// The OpenID Provider MUST validate the request received as follows: +/// +/// Authenticate the Client per the authentication method registered or configured for its client_id. +/// It is RECOMMENDED that Clients not send shared secrets in the Authentication Request but rather that public-key cryptography be used. +/// If the authentication request is signed, validate the JWT sent with the request parameter, which includes verifying the signature +/// and ensuring that the JWT is valid in all other respects per [RFC7519]. +/// +/// Validate all the authentication request parameters. In the event the request contains more than one of the hints specified +/// in Authentication Request, the OpenID Provider MUST return an "invalid_request" error response. +/// +/// The OpenID Provider MUST process the hint provided to determine if the hint is valid and if it corresponds to a valid user. +/// The type, issuer (where applicable) and maximum age (where applicable) of a hint that an OP accepts should be communicated +/// to Clients. +/// +/// If the hint is not valid or if the OP is not able to determine the user then an error should be returned to the Client +/// as per Section Authentication Error Response. +/// +/// The OpenID Provider MUST verify that all the REQUIRED parameters are present and their usage conforms to this specification. +/// +public interface IBackChannelAuthenticationRequestValidator +{ + Task ValidateAsync(BackChannelAuthenticationRequest request, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/ValidBackChannelAuthenticationRequest.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/ValidBackChannelAuthenticationRequest.cs new file mode 100644 index 00000000..4a09f08b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Interfaces/ValidBackChannelAuthenticationRequest.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; + +public record ValidBackChannelAuthenticationRequest(BackChannelAuthenticationRequest Model, ClientInfo Client) + : BackChannelAuthenticationValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/CheckSession/CheckSessionHandler.cs b/Abblix.Oidc.Server/Endpoints/CheckSession/CheckSessionHandler.cs new file mode 100644 index 00000000..9851e2a8 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/CheckSession/CheckSessionHandler.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Features.SessionManagement; + + +namespace Abblix.Oidc.Server.Endpoints.CheckSession; + +/// +/// Processes check session requests in accordance with OpenID Connect Session Management. +/// This class interacts with a session management service to determine the current authentication status +/// of an end-user with the OpenID Provider. It's an integral part of maintaining session integrity and security, +/// allowing the application to respond to changes in the user's authentication status in a timely and efficient manner. +/// +internal class CheckSessionHandler : ICheckSessionHandler +{ + /// + /// Initializes a new instance of the class. + /// Sets up the necessary session management service to handle OpenID Connect session checks. + /// + /// The service responsible for managing and verifying session states. + public CheckSessionHandler(ISessionManagementService sessionManagementService) + { + _sessionManagementService = sessionManagementService; + } + + private readonly ISessionManagementService _sessionManagementService; + + /// + /// + /// Processes a check session request asynchronously, leveraging the session management service + /// to assess the current state of the user's session. This method is key to ensuring that the + /// application's understanding of the user's session status is accurate and up-to-date. + /// + /// + /// A that, when completed, yields a + /// indicating the current state of the user's session. + /// + public Task HandleAsync() => _sessionManagementService.GetCheckSessionResponseAsync(); +} diff --git a/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/CheckSessionResponse.cs b/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/CheckSessionResponse.cs new file mode 100644 index 00000000..e78c7f96 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/CheckSessionResponse.cs @@ -0,0 +1,33 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; + +/// +/// Represents the response to a check session request in OpenID Connect Session Management. +/// This record contains the necessary information to ascertain the current state of a user session. +/// +/// The HTML content to be rendered, typically used in an iframe for session checking. +/// An object that represents a cache key, used for optimizing session state checks. +/// It serves as a key for caching the response to reduce frequent reevaluation when the session state is expected +/// to remain unchanged for an extended period, enhancing performance. +public record CheckSessionResponse(string HtmlContent, object CacheKey); diff --git a/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/ICheckSessionHandler.cs b/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/ICheckSessionHandler.cs new file mode 100644 index 00000000..0d737ba5 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/CheckSession/Interfaces/ICheckSessionHandler.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; + +/// +/// Represents an interface for creating a response to build the content of an OpenID Connect check-session frame (OP frame). +/// This interface defines a method for asynchronously processing the check session request and generating a response. +/// +public interface ICheckSessionHandler +{ + /// + /// Asynchronously processes the check session request and generates a response containing the content of the OP check-session frame. + /// + /// A task representing the response, which includes the HTML content of the check-session frame. + Task HandleAsync(); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ClientRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ClientRequestValidator.cs new file mode 100644 index 00000000..53bc1d1a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ClientRequestValidator.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// This class is responsible for validating client requests by checking the authorization header and client existence. +/// It implements the IClientRequestValidator interface. It uses an IClientInfoProvider to retrieve client information +/// and an IRegistrationAccessTokenValidator to validate the authorization header. +/// +public class ClientRequestValidator : IClientRequestValidator +{ + public ClientRequestValidator( + IClientInfoProvider clientInfoProvider, + IRegistrationAccessTokenValidator registrationAccessTokenValidator) + { + _clientInfoProvider = clientInfoProvider; + _registrationAccessTokenValidator = registrationAccessTokenValidator; + } + + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IRegistrationAccessTokenValidator _registrationAccessTokenValidator; + + /// + /// Validates a client request asynchronously by checking the authorization header and client existence. + /// + /// The client request to validate. + /// A task that represents the asynchronous operation. The task result contains the validation result. + public async Task ValidateAsync(ClientRequest request) + { + var headerErrorDescription = await _registrationAccessTokenValidator.ValidateAsync( + request.AuthorizationHeader, + request.ClientId.NotNull(nameof(request.ClientId))); + + if (headerErrorDescription != null) + return new ClientRequestValidationError(ErrorCodes.InvalidGrant, headerErrorDescription); + + var clientInfo = await _clientInfoProvider.TryFindClientAsync(request.ClientId).WithLicenseCheck(); + if (clientInfo == null) + return new ClientRequestValidationError(ErrorCodes.InvalidClient, "Client does not exist on this server"); + + return new ValidClientRequest(request, clientInfo); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationErrorResponse.cs new file mode 100644 index 00000000..f206cd37 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationErrorResponse.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents an error response for a client registration in the context of OpenID Connect. +/// +/// A string representing the error code or reason for the registration error. +/// A string providing additional details or a description of the registration error. +public record ClientRegistrationErrorResponse(string Error, string ErrorDescription) : ClientRegistrationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationRequestValidationResult.cs new file mode 100644 index 00000000..915a6f69 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationRequestValidationResult.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents the result of validating a client registration request in the context of OpenID Connect. +/// This is an abstract record used to encapsulate the outcome of client registration request validation. +/// +public abstract record ClientRegistrationRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationResponse.cs new file mode 100644 index 00000000..e3e60eb9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationResponse.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents the response for a client registration in the context of OpenID Connect. +/// This is an abstract record used as a base class for different types of client registration responses. +/// +public abstract record ClientRegistrationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationSuccessResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationSuccessResponse.cs new file mode 100644 index 00000000..53666cb2 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationSuccessResponse.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents a successful response for a client registration in the context of OpenID Connect. +/// +/// A string representing the unique identifier assigned to the registered client. +/// An optional DateTimeOffset indicating the time at which the client identifier was issued. +public record ClientRegistrationSuccessResponse(string ClientId, DateTimeOffset? ClientIdIssuedAt) + : ClientRegistrationResponse +{ + /// + /// The client secret assigned to the registered client. + /// + public string? ClientSecret { get; init; } + + /// + /// The expiration time of the client secret, if applicable. + /// + public DateTimeOffset? ClientSecretExpiresAt { get; init; } + + /// + /// The registration access token associated with the client registration, if applicable. + /// + public string? RegistrationAccessToken { get; init; } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationValidationError.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationValidationError.cs new file mode 100644 index 00000000..24a657bf --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRegistrationValidationError.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents a validation error for a client registration request in the context of OpenID Connect. +/// +/// A string representing the error code for the validation error. +/// A string providing a description of the validation error. +public record ClientRegistrationValidationError(string Error, string ErrorDescription) + : ClientRegistrationRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationError.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationError.cs new file mode 100644 index 00000000..d40d4e44 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationError.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents a validation error for a client request in the context of OpenID Connect. +/// +/// A string representing the error code for the validation error. +/// A string providing a description of the validation error. +public record ClientRequestValidationError(string Error, string ErrorDescription) + : ClientRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationResult.cs new file mode 100644 index 00000000..c60ba539 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ClientRequestValidationResult.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents the base class for the result of validating a client request in the context of OpenID Connect. +/// +public abstract record ClientRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IClientRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IClientRequestValidator.cs new file mode 100644 index 00000000..34c462a8 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IClientRequestValidator.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents an interface for validating client requests in the context of OpenID Connect. +/// +public interface IClientRequestValidator +{ + /// + /// Validates a client request asynchronously and returns a result of type ClientRequestValidationResult. + /// + /// The client request to validate. + /// A task representing the validation result. + Task ValidateAsync(ClientRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientHandler.cs new file mode 100644 index 00000000..c83734a2 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientHandler.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Defines a contract for handling requests to read client configurations, as part of client management in OAuth 2.0 +/// and OpenID Connect frameworks. +/// +public interface IReadClientHandler +{ + /// + /// Asynchronously handles a request to retrieve a client's configuration details. + /// + /// The client request containing the necessary information to identify the client + /// whose configuration is to be read. + /// A task that results in a , which may either contain the client's + /// configuration details if the request is valid, or an error response indicating the reason for failure. + /// + /// This method processes the incoming request to read a client's configuration. It first validates the request + /// to ensure that it meets the necessary criteria and that the client specified in the request exists and is + /// accessible by the requester. Upon successful validation, the method retrieves and returns the client's + /// configuration details. If the request is invalid or if the client cannot be found, an appropriate error + /// response is generated. + /// + Task HandleAsync(Model.ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientRequestProcessor.cs new file mode 100644 index 00000000..62b5e0bd --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IReadClientRequestProcessor.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents an interface for processing read client requests in the context of OpenID Connect. +/// +public interface IReadClientRequestProcessor +{ + /// + /// Processes a read client request asynchronously and returns a ReadClientResponse. + /// + /// The valid client request to process. + /// A task representing the processing result. + Task ProcessAsync(ValidClientRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientHandler.cs new file mode 100644 index 00000000..06e8d6d5 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientHandler.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Defines a contract for handling dynamic client registration requests in compliance with OAuth 2.0 and +/// OpenID Connect protocols. +/// +public interface IRegisterClientHandler +{ + /// + /// Asynchronously processes a client registration request, validating its content and, if valid, + /// registering the client with the authorization server. + /// + /// The client registration request containing the necessary parameters + /// for registering a new client, such as client metadata. + /// A task that results in a , encapsulating either the successful + /// registration details of the new client or an error response indicating the reasons for registration failure. + /// + /// + /// This method is responsible for the entire lifecycle of a client registration request, from initial validation + /// against the OAuth 2.0 and OpenID Connect specifications to processing the request and generating a response. + /// It ensures that all registered clients adhere to the protocol's requirements and the authorization server's + /// policies. + /// + Task HandleAsync(Model.ClientRegistrationRequest clientRegistrationRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestProcessor.cs new file mode 100644 index 00000000..a3a0cf5d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestProcessor.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Provides an interface for processing client registration requests. +/// +public interface IRegisterClientRequestProcessor +{ + /// + /// Processes the given client registration request asynchronously. + /// + /// The client registration request to process. + /// A task representing the asynchronous operation. The task result contains the client registration response. + Task ProcessAsync(ValidClientRegistrationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestValidator.cs new file mode 100644 index 00000000..5654dc43 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegisterClientRequestValidator.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Provides an interface for validating client registration requests. +/// +public interface IRegisterClientRequestValidator +{ + /// + /// Validates the given client registration request asynchronously. + /// + /// The client registration request to validate. + /// A task representing the asynchronous operation. The task result contains the validation result. + Task ValidateAsync(ClientRegistrationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegistrationAccessTokenValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegistrationAccessTokenValidator.cs new file mode 100644 index 00000000..2a5be3d1 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRegistrationAccessTokenValidator.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Headers; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Provides an interface for validating registration access tokens. +/// +public interface IRegistrationAccessTokenValidator +{ + /// + /// Validates the registration access token asynchronously. + /// + /// The authentication header containing the token. + /// The client ID associated with the token. + /// A task representing the asynchronous operation. The task result contains the validation result (token or null). + Task ValidateAsync(AuthenticationHeaderValue? header, string clientId); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientHandler.cs new file mode 100644 index 00000000..d2947db7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientHandler.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Defines a contract for handling requests to remove or unregister clients from the authorization server. +/// +public interface IRemoveClientHandler +{ + /// + /// Asynchronously processes a request for client removal, validating and executing the unregistration based + /// on the provided client information. + /// + /// + /// The client request containing the necessary information to identify the client to be removed. + /// A task that results in a , encapsulating the outcome of the client + /// removal process, which can be a confirmation of successful removal or details of any errors encountered. + /// + /// + /// This method is crucial for maintaining the security and integrity of the client registry within an OAuth 2.0 + /// and OpenID Connect framework. It ensures that only authorized and validated requests result in the removal of + /// a client, adhering to the standards and practices of dynamic client management. + /// + Task HandleAsync(ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientRequestProcessor.cs new file mode 100644 index 00000000..c4bcc69f --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/IRemoveClientRequestProcessor.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Defines a contract for processing requests to remove or deregister clients from an OAuth 2.0 or OpenID Connect +/// compliant system. +/// +/// +/// Implementations of this interface are responsible for the validation and execution of client removal operations, +/// ensuring that requests are authorized and that the removal aligns with security and protocol standards. +/// +public interface IRemoveClientRequestProcessor +{ + /// + /// Asynchronously processes a request to remove a client, ensuring the request is authorized and valid before + /// proceeding with the unregistration. + /// + /// A that has been validated and contains the necessary + /// information to identify and remove the specified client. + /// A that upon completion yields a , + /// indicating the outcome of the removal operation. + /// + /// This method is central to maintaining the integrity of the client registry by allowing for the removal + /// of clients that are no longer active or authorized. Implementations should ensure that the removal process + /// adheres to the system's security policies and the specifications of the OAuth 2.0 and OpenID Connect protocols. + /// + Task ProcessAsync(ValidClientRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientErrorResponse.cs new file mode 100644 index 00000000..fedabfc3 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientErrorResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents an error response for reading client information. +/// +public record ReadClientErrorResponse(string Error, string ErrorDescription) : ReadClientResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientResponse.cs new file mode 100644 index 00000000..7622bf1d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ReadClientResponse.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents the response for reading client information in a client management operation. +/// This is an abstract base class, and specific response types for successful and error cases +/// should inherit from this class. +/// +public abstract record ReadClientResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientErrorResponse.cs new file mode 100644 index 00000000..c9fc4911 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientErrorResponse.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents an error response for removing a client in a client management operation. +/// This record includes error information such as the error code and error description. +/// +public record RemoveClientErrorResponse(string Error, string ErrorDescription) : RemoveClientResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientResponse.cs new file mode 100644 index 00000000..41244fbd --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents the base response for removing a client in client management. +/// +public abstract record RemoveClientResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientSuccessfulResponse.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientSuccessfulResponse.cs new file mode 100644 index 00000000..a9bd2317 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/RemoveClientSuccessfulResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents a successful response for removing a client in client management. +/// +public record RemoveClientSuccessfulResponse : RemoveClientResponse; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRegistrationRequest.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRegistrationRequest.cs new file mode 100644 index 00000000..0371f132 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRegistrationRequest.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + + +/// +/// Represents a valid client registration request along with a sector identifier. +/// +public record ValidClientRegistrationRequest(ClientRegistrationRequest Model, string? SectorIdentifier) + : ClientRegistrationRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRequest.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRequest.cs new file mode 100644 index 00000000..385a4ee9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Interfaces/ValidClientRequest.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +/// +/// Represents a valid client request along with client information. +/// +public record ValidClientRequest(ClientRequest Model, ClientInfo ClientInfo) + : ClientRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientHandler.cs new file mode 100644 index 00000000..8da53117 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientHandler.cs @@ -0,0 +1,81 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles requests for reading client configurations from the authorization server. +/// Validates and processes requests to retrieve information about registered clients. +/// +public class ReadClientHandler : IReadClientHandler +{ + /// + /// Initializes a new instance of the class with specified validator and processor + /// services. + /// + /// The service used to validate client information requests. + /// The service responsible for processing valid client information requests and retrieving + /// client data. + public ReadClientHandler( + IClientRequestValidator validator, + IReadClientRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IClientRequestValidator _validator; + private readonly IReadClientRequestProcessor _processor; + + /// + /// Asynchronously handles a request to read client information, validating the request and processing it to return + /// the requested client data. + /// + /// The client request containing details necessary for fetching the client information, + /// such as the client identifier. + /// A task that results in a , which could be the requested client data or + /// an error response. + /// Thrown if the validation result does not match expected types. + /// + /// + /// This method serves as a critical part of dynamic client management, allowing for the secure retrieval of client + /// configurations. It ensures that only valid requests are processed, safeguarding against unauthorized access + /// to client information. + /// + public async Task HandleAsync(Model.ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(clientRequest); + + return validationResult switch + { + ValidClientRequest validRequest => await _processor.ProcessAsync(validRequest), + + ClientRequestValidationError { Error: var error, ErrorDescription: var description } + => new ReadClientErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()), + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientRequestProcessor.cs new file mode 100644 index 00000000..3e56aaaf --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/ReadClientRequestProcessor.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles the processing of requests to retrieve stored client information, ensuring that such requests +/// are valid and authorized. This class serves as a bridge between the request validation and the actual +/// retrieval of client details from the system's data store. +/// +public class ReadClientRequestProcessor : IReadClientRequestProcessor +{ + /// + /// Asynchronously retrieves the details of a client based on a valid request. + /// This method ensures that only authorized and valid requests lead to the disclosure of client information. + /// + /// A object containing the identification details + /// of the client whose information is being requested. + /// + /// A that, upon completion, yields a containing the details + /// of the client. The response includes information such as the client's ID, redirect URIs, and the URL for + /// initiating login, among other possible client configuration details. + /// + /// + /// This method is essential for supporting features like dynamic client registration and management in OAuth 2.0 + /// and OpenID Connect ecosystems. It allows clients or administrators to query the system for the configuration + /// of registered clients, facilitating transparency and ease of management. Note that sensitive information, + /// like client secrets, are not directly retrievable to maintain security. + /// + public Task ProcessAsync(ValidClientRequest request) + { + var client = request.ClientInfo; + + //TODO add missing properties + return Task.FromResult( + new ReadClientSuccessfulResponse + { + ClientId = client.ClientId, + //ClientSecret = we do not store secrets in initial forms, only hashes + RedirectUris = client.RedirectUris, + InitiateLoginUri = client.InitiateLoginUri, + }); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientHandler.cs new file mode 100644 index 00000000..463a9df9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientHandler.cs @@ -0,0 +1,86 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles dynamic registration of clients in accordance with OAuth 2.0 and OpenID Connect specifications. +/// This class validates and processes incoming client registration requests, issuing client identifiers and +/// client secrets as appropriate. +/// +public class RegisterClientHandler : IRegisterClientHandler +{ + /// + /// Initializes a new instance of the class with the specified validator + /// and processor. + /// + /// The validator responsible for ensuring that client registration requests meet + /// the required criteria. + /// The processor responsible for the actual registration of the client, + /// generating client identifiers and secrets. + public RegisterClientHandler( + IRegisterClientRequestValidator validator, + IRegisterClientRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IRegisterClientRequestValidator _validator; + private readonly IRegisterClientRequestProcessor _processor; + + /// + /// Asynchronously handles a client registration request, validating the request and processing it to register + /// the client. + /// + /// The client registration request containing the necessary information + /// for registering a new client. + /// A task that results in a , encapsulating the outcome of + /// the registration process. + /// This could be a successful response with client details or an error response indicating the reasons for failure. + /// + /// Thrown if the validation result is of an unexpected type, + /// indicating an implementation error. + /// + /// This method is central to the dynamic client registration feature, allowing clients to register themselves + /// with the authorization server without direct administrative intervention. It supports the OpenID Connect + /// Dynamic Client Registration specification, ensuring compliance and interoperability. + /// + + public async Task HandleAsync(Model.ClientRegistrationRequest clientRegistrationRequest) + { + var validationResult = await _validator.ValidateAsync(clientRegistrationRequest); + + return validationResult switch + { + ValidClientRegistrationRequest validRequest => await _processor.ProcessAsync(validRequest), + + ClientRegistrationValidationError { Error: var error, ErrorDescription: var description } + => new ClientRegistrationErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestProcessor.cs new file mode 100644 index 00000000..7c702f2a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestProcessor.cs @@ -0,0 +1,225 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using ClientRegistrationResponse = Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces.ClientRegistrationResponse; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles the registration of new clients by generating necessary credentials and adding client information to +/// the system. Ensures the secure and compliant registration of clients as per OAuth 2.0 and OpenID Connect standards. +/// +public class RegisterClientRequestProcessor : IRegisterClientRequestProcessor +{ + /// + /// Initializes a new instance of the with dependencies + /// required for client registration. + /// + /// Responsible for generating unique client IDs. + /// Responsible for generating secure client secrets. + /// Provides hashing services for client secrets. + /// Manages storage and retrieval of client information. + /// Provides time-related functionality, crucial for token issuance and expiration. + /// Configuration options for client registration, including secret length and expiration. + /// Formats JWTs for service use, including registration access tokens. + /// Provides issuer information, necessary for token issuance. + public RegisterClientRequestProcessor( + IClientIdGenerator clientIdGenerator, + IClientSecretGenerator clientSecretGenerator, + IHashService hashService, + IClientInfoManager clientInfoManager, + TimeProvider clock, + NewClientOptions options, + IAuthServiceJwtFormatter serviceJwtFormatter, + IIssuerProvider issuerProvider) + { + _clientIdGenerator = clientIdGenerator; + _clientSecretGenerator = clientSecretGenerator; + _hashService = hashService; + _clientInfoManager = clientInfoManager; + _clock = clock; + _options = options; + _serviceJwtFormatter = serviceJwtFormatter; + _issuerProvider = issuerProvider; + } + + private readonly IClientIdGenerator _clientIdGenerator; + private readonly IClientInfoManager _clientInfoManager; + private readonly IClientSecretGenerator _clientSecretGenerator; + private readonly IHashService _hashService; + private readonly TimeProvider _clock; + private readonly IIssuerProvider _issuerProvider; + private readonly NewClientOptions _options; + private readonly IAuthServiceJwtFormatter _serviceJwtFormatter; + + /// + /// Processes a valid client registration request, generating and storing the client's credentials and configuration. + /// + /// The client registration request containing the necessary details for registering + /// a new client. + /// A task that results in a , which includes the client ID, + /// client secret and registration access token, along with other registration details. + /// + /// This method orchestrates the client registration process, starting from generating a unique client ID + /// and secret (if required) to issuing a registration access token. It ensures that all registered clients + /// are compliant with the system's security standards and the OAuth 2.0 and OpenID Connect protocols. + /// The method also handles the storage of client information, facilitating future authentication and + /// authorization processes. + /// + public async Task ProcessAsync(ValidClientRegistrationRequest request) + { + var model = request.Model; + + var issuedAt = _clock.GetUtcNow(); + var clientId = model.ClientId.HasValue() ? model.ClientId : _clientIdGenerator.GenerateClientId(); + var (clientSecret, expiresAt) = GenerateClientSecret(model.TokenEndpointAuthMethod, issuedAt); + + var clientInfo = ToClientInfo(model, clientId, clientSecret, expiresAt, request.SectorIdentifier); + await _clientInfoManager.AddClientAsync(clientInfo); + + var response = new ClientRegistrationSuccessResponse(clientId, issuedAt) + { + ClientSecret = clientSecret, + ClientSecretExpiresAt = expiresAt, + RegistrationAccessToken = await IssueRegistrationAccessTokenAsync(clientId, issuedAt), + }; + return response; + } + + private (string? clientSecret, DateTimeOffset? expiresAt) GenerateClientSecret( + string tokenEndpointAuthMethod, + DateTimeOffset issuedAt) + { + switch (tokenEndpointAuthMethod) + { + case ClientAuthenticationMethods.ClientSecretBasic: + case ClientAuthenticationMethods.ClientSecretPost: + var clientSecret = _clientSecretGenerator.GenerateClientSecret(_options.ClientSecret.Length); + var expiresAt = issuedAt + _options.ClientSecret.ExpiresAfter; + return (clientSecret, expiresAt); + + default: + // It is not needed for Clients selecting a token_endpoint_auth_method of private_key_jwt unless symmetric encryption will be used. + return (clientSecret: null, expiresAt: null); + } + } + + private ClientInfo ToClientInfo( + ClientRegistrationRequest model, + string clientId, + string? clientSecret, + DateTimeOffset? expiresAt, + string? sectorIdentifier) + { + var clientInfo = new ClientInfo(clientId) + { + ClientSecrets = clientSecret.HasValue() + ? new[] + { + new ClientSecret + { + Sha512Hash = _hashService.Sha(HashAlgorithm.Sha512, clientSecret), + ExpiresAt = expiresAt, + } + } + : null, + + TokenEndpointAuthMethod = model.TokenEndpointAuthMethod, + AllowedResponseTypes = model.ResponseTypes, + AllowedGrantTypes = model.GrantTypes, + RedirectUris = model.RedirectUris, + Jwks = model.Jwks, + JwksUri = model.JwksUri, + PkceRequired = model.PkceRequired, + OfflineAccessAllowed = model.OfflineAccessAllowed, + LogoUri = model.LogoUri, + PolicyUri = model.PolicyUri, + TermsOfServiceUri = model.TermsOfServiceUri, + InitiateLoginUri = model.InitiateLoginUri, + SubjectType = model.SubjectType, + SectorIdentifier = sectorIdentifier, + PostLogoutRedirectUris = model.PostLogoutRedirectUris, + }; + + if (model.UserInfoSignedResponseAlg.HasValue()) + { + clientInfo.UserInfoSignedResponseAlgorithm = model.UserInfoSignedResponseAlg; + } + + if (model.IdTokenSignedResponseAlg.HasValue()) + { + clientInfo.IdentityTokenSignedResponseAlgorithm = model.IdTokenSignedResponseAlg; + } + + if (model.BackChannelLogoutUri != null) + { + clientInfo.BackChannelLogout = new BackChannelLogoutOptions( + model.BackChannelLogoutUri, + model.BackChannelLogoutSessionRequired); + } + + if (model.FrontChannelLogoutUri != null) + { + clientInfo.FrontChannelLogout = new FrontChannelLogoutOptions( + model.FrontChannelLogoutUri, + model.FrontChannelLogoutSessionRequired); + } + + return clientInfo; + } + + private Task IssueRegistrationAccessTokenAsync(string clientId, DateTimeOffset issuedAt) + { + var token = new JsonWebToken + { + Header = + { + Type = JwtTypes.RegistrationAccessToken, + Algorithm = SigningAlgorithms.RS256, + }, + Payload = + { + IssuedAt = issuedAt, + NotBefore = issuedAt, + //ExpiresAt = issuedAt + ..., //TODO think about the expiration of this token + + Issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()), + Audiences = new[] { clientId }, + Subject = clientId, + }, + }; + + return _serviceJwtFormatter.FormatAsync(token); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestValidator.cs new file mode 100644 index 00000000..543b5c7e --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegisterClientRequestValidator.cs @@ -0,0 +1,72 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Provides validation for client registration requests to ensure they meet the system's criteria for client +/// registrations. Utilizes an underlying context validator for comprehensive evaluation of request parameters. +/// +public class RegisterClientRequestValidator : IRegisterClientRequestValidator +{ + /// + /// Instantiates a new with a specific context validator for + /// evaluating client registration requests. + /// + /// An implementation of used to assess + /// the validity of registration requests based on predefined criteria and the system's registration policies. + /// + public RegisterClientRequestValidator(IClientRegistrationContextValidator validator) + { + _validator = validator; + } + + private readonly IClientRegistrationContextValidator _validator; + + /// + /// Asynchronously validates a client registration request against the system's registration policies and criteria. + /// + /// The containing the details of the client seeking + /// registration. + /// + /// A that when completed will yield a , + /// which may either indicate a successful validation through a + /// instance or detail any issues encountered during validation as a + /// . + /// + /// + /// This method orchestrates the validation process by creating a validation context from the provided request + /// and passing it to the context validator for evaluation. It ensures that only requests fulfilling all necessary + /// conditions are considered valid, thus maintaining the integrity and security of the client registration process. + /// + + public async Task ValidateAsync(ClientRegistrationRequest request) + { + var context = new ClientRegistrationValidationContext(request); + ClientRegistrationRequestValidationResult? error = await _validator.ValidateAsync(context); + return error ?? new ValidClientRegistrationRequest(request, context.SectorIdentifier); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegistrationAccessTokenValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegistrationAccessTokenValidator.cs new file mode 100644 index 00000000..b77a1840 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RegistrationAccessTokenValidator.cs @@ -0,0 +1,106 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Headers; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using HttpRequestHeaders = Abblix.Oidc.Server.Common.Constants.HttpRequestHeaders; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles the validation of registration access tokens, ensuring they adhere to JWT standards and +/// are authorized for use by specific clients. +/// +public class RegistrationAccessTokenValidator : IRegistrationAccessTokenValidator +{ + /// + /// Constructs a new instance of the , equipped with a JWT validation + /// service for verifying the integrity and authorization of registration access tokens. + /// + /// An implementation of responsible for the + /// JWT validation logic. + public RegistrationAccessTokenValidator(IAuthServiceJwtValidator jwtValidator) + { + _jwtValidator = jwtValidator; + } + + private readonly IAuthServiceJwtValidator _jwtValidator; + + /// + /// Asynchronously validates a registration access token, verifying its format, type, and authorization for + /// the intended client. + /// + /// The HTTP Authorization header containing the bearer token to be validated. + /// The unique identifier of the client that the token is supposed to authorize. + /// + /// A task that results in a nullable string; returns null if the token is valid and authorized for the specified + /// client, or an error message detailing the reason for validation failure. + /// + /// + /// This method is crucial for securing client registration and management endpoints by ensuring that only valid + /// and authorized registration access tokens can perform operations. It checks the token's type, audience + /// and subject against the expected values, employing JWT validation to ascertain its integrity and applicability. + /// + public async Task ValidateAsync(AuthenticationHeaderValue? header, string clientId) + { + if (header?.Parameter == null) + return $"The access token must be specified via '{HttpRequestHeaders.Authorization}' header"; + + if (header.Scheme != TokenTypes.Bearer) + return $"The scheme name '{header.Scheme}' is not supported"; + + var result = await _jwtValidator.ValidateAsync( + header.Parameter, + ValidationOptions.Default & ~ValidationOptions.ValidateAudience); + + switch (result) + { + case ValidJsonWebToken + { + Token: + { + Header.Type: var tokenType, + Payload: { Audiences: var audiences, Subject: var subject }, + } + }: + if (tokenType != JwtTypes.RegistrationAccessToken) + return $"Invalid token type: {tokenType}"; + + if (subject != clientId || !audiences.Contains(clientId)) + return "The access token unauthorized"; + + break; + + case JwtValidationError error: + return error.ErrorDescription; + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + + return default; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientHandler.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientHandler.cs new file mode 100644 index 00000000..1486af93 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientHandler.cs @@ -0,0 +1,85 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Manages the removal of registered clients from the authorization server, ensuring that requests for client +/// unregistration are handled securely and in accordance with OAuth 2.0 and OpenID Connect standards. +/// +public class RemoveClientHandler : IRemoveClientHandler +{ + /// + /// Initializes a new instance of the class with the specified validator + /// and processor services. + /// + /// The service used to validate incoming client removal requests. + /// The service responsible for processing validated removal requests and unregistering + /// the client. + public RemoveClientHandler( + IClientRequestValidator validator, + IRemoveClientRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IClientRequestValidator _validator; + private readonly IRemoveClientRequestProcessor _processor; + + /// + /// Asynchronously processes a request to remove a registered client, ensuring the request is valid and authorized + /// before proceeding with client unregistration. + /// + /// The client request containing necessary information for identifying and removing + /// the specified client. + /// A task that results in a , which encapsulates the outcome of + /// the removal process, including success or error information. + /// + /// Thrown if the result of the validation process is of an unexpected type, indicating a potential issue + /// in the request handling pipeline. + /// + /// This method follows a two-step process: first, validating the removal request to ensure it meets all + /// required criteria and is authorized; second, if validation succeeds, processing the request to unregister + /// the client from the system. This approach ensures that client removal is managed securely and aligns with + /// best practices in client management within OAuth 2.0 and OpenID Connect frameworks. + /// + public async Task HandleAsync(ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(clientRequest); + + var response = validationResult switch + { + ValidClientRequest validRequest => await _processor.ProcessAsync(validRequest), + + ClientRequestValidationError { Error: var error, ErrorDescription: var description } + => new RemoveClientErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientRequestProcessor.cs new file mode 100644 index 00000000..e31c65a7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/RemoveClientRequestProcessor.cs @@ -0,0 +1,67 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement; + +/// +/// Handles the backend logic for processing requests to remove clients from the system. This processor uses the provided +/// client information manager to execute the removal operation. +/// +public class RemoveClientRequestProcessor : IRemoveClientRequestProcessor +{ + /// + /// Constructs a new instance of the class, initializing it with + /// the necessary components to manage client information and perform removal operations. + /// + /// An instance of used to interact with and + /// manage client information, facilitating the removal of clients based on their identifiers. + public RemoveClientRequestProcessor(IClientInfoManager clientInfoManager) + { + _clientInfoManager = clientInfoManager; + } + + private readonly IClientInfoManager _clientInfoManager; + + /// + /// Asynchronously executes the process of removing a client based on the provided request. + /// This method ensures the client specified in the request is removed from the system, + /// reflecting changes in the client registry. + /// + /// An instance of containing the details of + /// the client to be removed, including its unique identifier. + /// A task that, upon completion, yields a indicating the successful + /// removal of the client. + /// + /// This method calls upon the to remove the specified client. + /// It is expected that the request passed to this method has been validated beforehand, + /// ensuring that the client exists and the initiator of the request has the authority to perform + /// the removal operation. + /// + public async Task ProcessAsync(ValidClientRequest request) + { + await _clientInfoManager.RemoveClientAsync(request.ClientInfo.ClientId); + return new RemoveClientSuccessfulResponse(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientIdValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientIdValidator.cs new file mode 100644 index 00000000..2eaced85 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientIdValidator.cs @@ -0,0 +1,71 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class validates the client ID specified in a client registration request by checking if it exists and is authorized. +/// +public class ClientIdValidator : IClientRegistrationContextValidator +{ + /// + /// Initializes a new instance of the ClientIdValidator class with a client info provider and a logger. + /// + /// The client info provider to retrieve client information. + /// The logger to be used for logging purposes. + public ClientIdValidator( + IClientInfoProvider clientInfoProvider, + ILogger logger) + { + _clientInfoProvider = clientInfoProvider; + _logger = logger; + } + + private readonly IClientInfoProvider _clientInfoProvider; + private readonly ILogger _logger; + + /// + /// Validates the client specified in the client registration request by checking if it already exists and is registered. + /// + /// The validation context containing client registration information. + /// A ClientRegistrationValidationError if the validation fails, or null if the request is valid. + public async Task ValidateAsync(ClientRegistrationValidationContext context) + { + var clientId = context.Request.ClientId; + if (clientId.HasValue()) + { + var clientInfo = await _clientInfoProvider.TryFindClientAsync(clientId).WithLicenseCheck(); + if (clientInfo != null) + { + _logger.LogWarning("The client with id {ClientId} is already registered", clientId); + return ErrorFactory.InvalidClientMetadata($"The client with id={clientId} is already registered"); + } + } + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationContextValidatorComposite.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationContextValidatorComposite.cs new file mode 100644 index 00000000..9692d9d4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationContextValidatorComposite.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class represents a composite validator for client registration requests. +/// It aggregates multiple validation steps and executes them sequentially. +/// +public class ClientRegistrationContextValidatorComposite : IClientRegistrationContextValidator +{ + /// + /// Initializes a new instance of the class with an array of validation steps. + /// + /// The array of validation steps to be executed. + public ClientRegistrationContextValidatorComposite(IClientRegistrationContextValidator[] validationSteps) + { + _validationSteps = validationSteps; + } + + private readonly IClientRegistrationContextValidator[] _validationSteps; + + /// + /// Validates the client registration request by executing each validation step in the specified order. + /// + /// The validation context containing client registration information. + /// A ClientRegistrationValidationError if any validation step fails, or null if the request is valid. + public async Task ValidateAsync(ClientRegistrationValidationContext context) + { + foreach (var validationStep in _validationSteps) + { + var error = await validationStep.ValidateAsync(context); + if (error != null) + return error; + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationValidationContext.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationValidationContext.cs new file mode 100644 index 00000000..4803368c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ClientRegistrationValidationContext.cs @@ -0,0 +1,37 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// Represents a validation context used during the client registration validation process. +/// It contains information about the client registration request and the sector identifier. +/// +public record ClientRegistrationValidationContext(ClientRegistrationRequest Request) +{ + /// + /// Gets or sets the sector identifier associated with the client. + /// + public string? SectorIdentifier { get; set; } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ErrorFactory.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ErrorFactory.cs new file mode 100644 index 00000000..aa3d9298 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/ErrorFactory.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// A static factory class for creating instances of `ClientRegistrationValidationError`. +/// It provides methods to generate validation errors with specific error codes and descriptions. +/// +public static class ErrorFactory +{ + /// + /// Creates a validation error for an invalid redirect URI. + /// + /// The description of the error. + /// An error instance with the error code and description. + public static ClientRegistrationValidationError InvalidRedirectUri(string description) + => new(ErrorCodes.InvalidRedirectUri, description); + + /// + /// Creates a validation error for invalid client metadata. + /// + /// The description of the error. + /// An error instance with the error code and description. + public static ClientRegistrationValidationError InvalidClientMetadata(string description) + => new(ErrorCodes.InvalidClientMetadata, description); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/GrantTypeValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/GrantTypeValidator.cs new file mode 100644 index 00000000..35d2534e --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/GrantTypeValidator.cs @@ -0,0 +1,68 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class is responsible for validating grant types in the context of client registration. +/// It checks that the grant types, based on the specified response types, +/// are present in the client's registration request. If any grant types are missing, +/// it generates a validation error indicating which grant types are required. +/// This validation ensures that clients are registered with appropriate grant types for OAuth 2.0 flows. +/// +public class GrantTypeValidator : SyncClientRegistrationContextValidator +{ + /// + /// Validates grant types based on response types in the client registration request. + /// + /// The validation context containing client registration data. + /// + /// A ClientRegistrationValidationError if grant types are missing, or null if the request is valid. + /// + protected override ClientRegistrationValidationError? Validate(ClientRegistrationValidationContext context) + { + var request = context.Request; + var requiredGrantTypes = new HashSet(); + + foreach (var responseType in request.ResponseTypes) + { + if (responseType.HasFlag(ResponseTypes.Code)) + requiredGrantTypes.Add(GrantTypes.AuthorizationCode); + + if (responseType.HasFlag(ResponseTypes.Token) || responseType.HasFlag(ResponseTypes.IdToken)) + requiredGrantTypes.Add(GrantTypes.Implicit); + } + + var missingGrantTypes = requiredGrantTypes.Except(request.GrantTypes).ToArray(); + if (missingGrantTypes.Length > 0) + { + return ErrorFactory.InvalidClientMetadata( + $"The following grant types are required: {string.Join(", ", missingGrantTypes)}"); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/IClientRegistrationContextValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/IClientRegistrationContextValidator.cs new file mode 100644 index 00000000..ec4b24c8 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/IClientRegistrationContextValidator.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// Provides an interface for client registration context validators. Implementations of this interface +/// are responsible for validating various aspects of client registration requests, such as client identifiers, +/// redirect URIs, grant types, and more. The validators check the validity of client registration data and +/// may return validation errors if the data is invalid. +/// +public interface IClientRegistrationContextValidator +{ + /// + /// Validates the client registration context asynchronously. + /// + /// The context containing client registration data to validate. + /// + /// A task that represents the asynchronous validation operation. The task result is a + /// ClientRegistrationValidationError if validation fails, or null if the request is valid. + /// + Task ValidateAsync(ClientRegistrationValidationContext context); +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/InitiateLoginUriValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/InitiateLoginUriValidator.cs new file mode 100644 index 00000000..65955b69 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/InitiateLoginUriValidator.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using static Abblix.Oidc.Server.Model.ClientRegistrationRequest; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class is responsible for validating the Initiate Login URI specified in the client registration request. +/// It ensures that the URI is an absolute HTTPS URI. +/// +public class InitiateLoginUriValidator: SyncClientRegistrationContextValidator +{ + /// + /// Validates the Initiate Login URI specified in the client registration request. + /// + /// The validation context containing client registration data. + /// + /// A ClientRegistrationValidationError if the validation fails, or null if the request is valid. + /// + protected override ClientRegistrationValidationError? Validate(ClientRegistrationValidationContext context) + { + var model = context.Request; + if (model.InitiateLoginUri != null) + { + if (!model.InitiateLoginUri.IsAbsoluteUri) + { + return ErrorFactory.InvalidClientMetadata($"The {Parameters.InitiateLoginUri} is not an absolute URI"); + } + + if (model.InitiateLoginUri.Scheme != Uri.UriSchemeHttps) + { + return ErrorFactory.InvalidClientMetadata($"The {Parameters.InitiateLoginUri} must have HTTPS scheme"); + } + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/PostLogoutRedirectUrisValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/PostLogoutRedirectUrisValidator.cs new file mode 100644 index 00000000..adfa7ac5 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/PostLogoutRedirectUrisValidator.cs @@ -0,0 +1,96 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Utils; +using static Abblix.Oidc.Server.Model.ClientRegistrationRequest; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class validates Post Logout Redirect URIs in a client registration request. It checks if the URIs are absolute, +/// do not contain fragments, and comply with security requirements based on the application type. +/// If any validation fails, it returns a ClientRegistrationValidationError. +/// +public class PostLogoutRedirectUrisValidator : SyncClientRegistrationContextValidator +{ + /// + /// Validates Post Logout Redirect URIs in the client registration request. + /// + /// The validation context containing client registration data. + /// + /// A ClientRegistrationValidationError if any validation fails, or null if the request is valid. + /// + protected override ClientRegistrationValidationError? Validate(ClientRegistrationValidationContext context) + { + var request = context.Request; + + foreach (var uri in request.PostLogoutRedirectUris) + { + if (uri is not { IsAbsoluteUri : true }) + return ErrorFactory.InvalidRedirectUri( + $"{Parameters.PostLogoutRedirectUris} must contain only absolute URIs"); + + if (uri.Fragment.HasValue()) + return ErrorFactory.InvalidRedirectUri($"{Parameters.PostLogoutRedirectUris} must not contain fragment"); + + var applicationType = context.Request.ApplicationType; + switch (applicationType) + { + case ApplicationTypes.Web when uri.Scheme != Uri.UriSchemeHttps: + // Web Clients using the OAuth Implicit Grant Type MUST only register URLs using the https scheme as redirect_uris + return ErrorFactory.InvalidRedirectUri( + $"{Parameters.PostLogoutRedirectUris} must be secure ({Uri.UriSchemeHttps})"); + + case ApplicationTypes.Web when IsLocalhost(uri): + // they MUST NOT use localhost as the hostname + return ErrorFactory.InvalidRedirectUri( + $"{Parameters.PostLogoutRedirectUris} must not use host name {uri.Host}"); + + case ApplicationTypes.Web: + break; + + case ApplicationTypes.Native when uri.Scheme == Uri.UriSchemeHttp && !IsLocalhost(uri): + // Native Clients MUST only register redirect_uris using the http: scheme with localhost as the hostname + return ErrorFactory.InvalidRedirectUri( + $"Native Clients MUST only register {Parameters.PostLogoutRedirectUris} using the http: scheme with localhost as the hostname"); + + case ApplicationTypes.Native when uri.Scheme == Uri.UriSchemeHttps: + // Native Clients MUST only register redirect_uris using custom URI schemes + return ErrorFactory.InvalidRedirectUri( + $"Native Clients MUST only register {Parameters.PostLogoutRedirectUris} using custom URI schemes"); + + case ApplicationTypes.Native: + break; + + default: + throw new UnexpectedTypeException(nameof(applicationType), applicationType.GetType()); + } + } + + return null; + } + + private static bool IsLocalhost(Uri uri) => uri.IsLoopback || uri.Host == "localhost"; +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/RedirectUrisValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/RedirectUrisValidator.cs new file mode 100644 index 00000000..982f76b6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/RedirectUrisValidator.cs @@ -0,0 +1,98 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Utils; +using static Abblix.Oidc.Server.Model.ClientRegistrationRequest; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This internal class validates Redirect URIs in a client registration request. It checks if the URIs are absolute, +/// do not contain fragments, and comply with security requirements based on the application type. +/// If any validation fails, it returns a ClientRegistrationValidationError. +/// +internal class RedirectUrisValidator : SyncClientRegistrationContextValidator +{ + /// + /// Validates Redirect URIs in the client registration request. + /// + /// The validation context containing client registration data. + /// + /// A ClientRegistrationValidationError if any validation fails, or null if the request is valid. + /// + protected override ClientRegistrationValidationError? Validate(ClientRegistrationValidationContext context) + { + var request = context.Request; + + if (request.RedirectUris.Length == 0) + return ErrorFactory.InvalidRedirectUri($"{Parameters.RedirectUris} is required"); + + foreach (var uri in request.RedirectUris) + { + if (uri is not { IsAbsoluteUri : true }) + return ErrorFactory.InvalidRedirectUri($"{Parameters.RedirectUris} must contain only absolute URIs"); + + if (uri.Fragment.HasValue()) + return ErrorFactory.InvalidRedirectUri($"{Parameters.RedirectUris} must not contain fragment"); + + var applicationType = context.Request.ApplicationType; + switch (applicationType) + { + case ApplicationTypes.Web when uri.Scheme != Uri.UriSchemeHttps: + // Web Clients using the OAuth Implicit Grant Type MUST only register URLs using the https scheme as redirect_uris + return ErrorFactory.InvalidRedirectUri( + $"{Parameters.RedirectUris} must be secure ({Uri.UriSchemeHttps})"); + + case ApplicationTypes.Web when IsLocalhost(uri): + // they MUST NOT use localhost as the hostname + return ErrorFactory.InvalidRedirectUri( + $"{Parameters.RedirectUris} must not use host name {uri.Host}"); + + case ApplicationTypes.Web: + break; + + case ApplicationTypes.Native when uri.Scheme == Uri.UriSchemeHttp && !IsLocalhost(uri): + // Native Clients MUST only register redirect_uris using the http: scheme with localhost as the hostname + return ErrorFactory.InvalidRedirectUri( + $"Native Clients MUST only register {Parameters.RedirectUris} using the http: scheme with localhost as the hostname"); + + case ApplicationTypes.Native when uri.Scheme == Uri.UriSchemeHttps: + // Native Clients MUST only register redirect_uris using custom URI schemes + return ErrorFactory.InvalidRedirectUri( + $"Native Clients MUST only register {Parameters.RedirectUris} using custom URI schemes"); + + case ApplicationTypes.Native: + break; + + default: + throw new UnexpectedTypeException(nameof(applicationType), applicationType.GetType()); + } + } + + return null; + } + + private static bool IsLocalhost(Uri uri) => uri.IsLoopback || uri.Host == "localhost"; +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SubjectTypeValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SubjectTypeValidator.cs new file mode 100644 index 00000000..9b50ec03 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SubjectTypeValidator.cs @@ -0,0 +1,141 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Json; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Microsoft.Extensions.Logging; +using static Abblix.Oidc.Server.Model.ClientRegistrationRequest; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This class validates the subject type in a client registration request. It checks if the subject type is pairwise, +/// and if so, verifies the sector identifier URI and its content. It also ensures that all redirect URIs use the HTTPS scheme. +/// If any validation fails, it returns a ClientRegistrationValidationError. +/// +public class SubjectTypeValidator: IClientRegistrationContextValidator +{ + /// + /// Initializes a new instance of the SubjectTypeValidator class with the provided dependencies. + /// + /// The HttpClientFactory for making HTTP requests. + /// The logger for logging purposes. + public SubjectTypeValidator( + IHttpClientFactory httpClientFactory, + ILogger logger) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + } + + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + + /// + /// Validates the subject type in the client registration request. + /// + /// The validation context containing client registration data. + /// + /// A ClientRegistrationValidationError if any validation fails, or null if the request is valid. + /// + public async Task ValidateAsync(ClientRegistrationValidationContext context) + { + var request = context.Request; + if (request.SubjectType == SubjectTypes.Pairwise) + { + var sectorIdentifierUri = request.SectorIdentifierUri; + if (sectorIdentifierUri != null) + { + if (!sectorIdentifierUri.IsAbsoluteUri) + { + return ErrorFactory.InvalidClientMetadata( + $"{Parameters.SectorIdentifierUri} must be absolute URI"); + } + if (sectorIdentifierUri.Scheme != Uri.UriSchemeHttps) + { + return ErrorFactory.InvalidClientMetadata( + $"{Parameters.SectorIdentifierUri} must have {Uri.UriSchemeHttps} scheme"); + } + + Uri[]? sectorIdentifierUriContent; + { + try + { + // TODO move to separate class + sectorIdentifierUriContent = await _httpClientFactory.CreateClient().GetFromJsonAsync( + sectorIdentifierUri); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unable to receive content of {SectorIdentifierUri}", + sectorIdentifierUri); + return ErrorFactory.InvalidClientMetadata( + $"Unable to receive content of {Parameters.SectorIdentifierUri}"); + } + } + + if (sectorIdentifierUriContent is not { Length: > 0 }) + { + return ErrorFactory.InvalidClientMetadata( + $"The content of {Parameters.SectorIdentifierUri} is empty"); + } + + if (sectorIdentifierUriContent.Any(uri => uri.Scheme != Uri.UriSchemeHttps)) + { + return ErrorFactory.InvalidClientMetadata("All schemes in the redirect URIs must be https"); + } + + var missingUris = sectorIdentifierUriContent.Except(request.RedirectUris).ToArray(); + if (missingUris.Length > 0) + { + _logger.LogWarning("The following URIs are present in the {SectorIdentifierUri}, but missing from the Redirect URIs: {@MissingUris}", + sectorIdentifierUri, + missingUris); + + return ErrorFactory.InvalidClientMetadata( + $"The content received from the {Parameters.SectorIdentifierUri} contains one or more URIs that are not in the registered list of redirect URIs"); + } + + context.SectorIdentifier = sectorIdentifierUri.Host; + } + else + { + if (request.RedirectUris.Any(uri => uri.Scheme != Uri.UriSchemeHttps)) + { + return ErrorFactory.InvalidClientMetadata("All schemes in the redirect URIs must be https"); + } + + var hosts = request.RedirectUris.Select(uri => uri.Host).Distinct().ToArray(); + if (hosts.Length > 1) + { + return ErrorFactory.InvalidRedirectUri("The client specified pairwise subject type, but provides several redirect URIs with different hosts"); + } + + var sectorIdentifier = hosts[0]; + context.SectorIdentifier = sectorIdentifier; + } + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SyncClientRegistrationContextValidator.cs b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SyncClientRegistrationContextValidator.cs new file mode 100644 index 00000000..fb0ac108 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/DynamicClientManagement/Validation/SyncClientRegistrationContextValidator.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; + +/// +/// This abstract class provides a base for synchronous client registration context validators. +/// It implements the ValidateAsync method from the IClientRegistrationContextValidator interface +/// by calling the abstract Validate method and wrapping the result in a Task. +/// +public abstract class SyncClientRegistrationContextValidator : IClientRegistrationContextValidator +{ + public Task ValidateAsync(ClientRegistrationValidationContext context) + => Task.FromResult(Validate(context)); + + /// + /// Validates the client registration context synchronously and returns a ClientRegistrationValidationError if validation fails, + /// or null if the context is valid. Derived classes must implement this method. + /// + /// The validation context containing client registration information. + /// A ClientRegistrationValidationError if validation fails, or null if the context is valid. + protected abstract ClientRegistrationValidationError? Validate(ClientRegistrationValidationContext context); +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionHandler.cs b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionHandler.cs new file mode 100644 index 00000000..0ebc7c90 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionHandler.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.EndSession; + +public class EndSessionHandler : IEndSessionHandler +{ + public EndSessionHandler( + IEndSessionRequestValidator validator, + IEndSessionRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IEndSessionRequestValidator _validator; + private readonly IEndSessionRequestProcessor _processor; + + public async Task HandleAsync(Model.EndSessionRequest endSessionRequest) + { + var validationResult = await _validator.ValidateAsync(endSessionRequest); + + var response = validationResult switch + { + ValidEndSessionRequest validRequest => await _processor.ProcessAsync(validRequest), + + EndSessionRequestValidationError { Error: var error, ErrorDescription: var description } + => new EndSessionErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestProcessor.cs new file mode 100644 index 00000000..e154d0fc --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestProcessor.cs @@ -0,0 +1,133 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.LogoutNotification; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + + +namespace Abblix.Oidc.Server.Endpoints.EndSession; + +/// +/// Implements the logic for processing end-session requests. +/// +/// +/// This class is responsible for handling end-session requests. It facilitates user logout, client notifications, +/// and ensures compliance with the relevant OAuth 2.0 and OpenID Connect standards. +/// +public class EndSessionRequestProcessor : IEndSessionRequestProcessor +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The authentication service. + /// The issuer provider. + /// The client info provider. + /// The logout notifier. + public EndSessionRequestProcessor( + ILogger logger, + IAuthSessionService authSessionService, + IIssuerProvider issuerProvider, + IClientInfoProvider clientInfoProvider, + ILogoutNotifier logoutNotifier) + { + _logger = logger; + _authSessionService = authSessionService; + _issuerProvider = issuerProvider; + _clientInfoProvider = clientInfoProvider; + _logoutNotifier = logoutNotifier; + } + + private readonly ILogger _logger; + private readonly IAuthSessionService _authSessionService; + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IIssuerProvider _issuerProvider; + private readonly ILogoutNotifier _logoutNotifier; + + /// + /// Processes the end-session request and returns the corresponding response. + /// + /// The valid end-session request to be processed. + /// A task representing the asynchronous operation, which upon completion will yield the . + public async Task ProcessAsync(ValidEndSessionRequest request) + { + var postLogoutRedirectUri = request.Model.PostLogoutRedirectUri; + if (postLogoutRedirectUri != null && request.Model.State != null) + { + postLogoutRedirectUri = new UriBuilder(postLogoutRedirectUri) + { + Query = + { + [Parameters.State] = request.Model.State, + } + }; + } + + var authSession = await _authSessionService.AuthenticateAsync(); + if (authSession == null) + { + return new EndSessionSuccessfulResponse(postLogoutRedirectUri, Array.Empty()); + } + + var sessionId = authSession.SessionId; + + var subjectId = authSession.Subject; + if (!subjectId.HasValue()) + { + throw new InvalidOperationException( + $"The claim {JwtClaimTypes.Subject} must contain the unique identifier of the user logged in"); + } + + await _authSessionService.SignOutAsync(); + _logger.LogDebug("The user with subject={Subject} was logged out from session {Session}", subjectId, sessionId); + + var context = new LogoutContext(sessionId, subjectId, LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer())); + + var tasks = new List(); + foreach (var clientId in authSession.AffectedClientIds) + { + var clientInfo = await _clientInfoProvider.TryFindClientAsync(clientId).WithLicenseCheck(); + if (clientInfo == null) + continue; + + var task = _logoutNotifier.NotifyClientAsync(clientInfo, context); + if (task.Status == TaskStatus.Running) + tasks.Add(task); + } + await Task.WhenAll(tasks); + + var response = new EndSessionSuccessfulResponse(postLogoutRedirectUri, context.FrontChannelLogoutRequestUris); + return response; + } + + private static class Parameters + { + public const string State = "state"; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestValidator.cs new file mode 100644 index 00000000..afa09f9f --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/EndSessionRequestValidator.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Endpoints.EndSession.Validation; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.EndSession; + +/// +/// Implements the logic for validating end-session requests. +/// +/// +/// This class validates end-session requests to ensure they conform to expected standards and business rules. +/// It uses the injected for performing the actual validation logic. +/// Depending on the validation outcome, it constructs an appropriate validation result which can indicate either +/// successful validation or a specific error condition. +/// +public class EndSessionRequestValidator : IEndSessionRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// + /// The end-session context validator responsible for the core validation logic. + public EndSessionRequestValidator(IEndSessionContextValidator validator) + { + _validator = validator; + } + + private readonly IEndSessionContextValidator _validator; + + /// + /// + /// Validates the specified end-session request asynchronously. + /// + /// The end-session request to be validated. + /// + /// A task representing the asynchronous validation operation. The task result contains the + /// which encapsulates the validation outcome. + /// + public async Task ValidateAsync(EndSessionRequest request) + { + var context = new EndSessionValidationContext(request); + + var result = await _validator.ValidateAsync(context) ?? + (EndSessionRequestValidationResult)new ValidEndSessionRequest(context.Request, context.ClientInfo); + + return result; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/IEndSessionHandler.cs b/Abblix.Oidc.Server/Endpoints/EndSession/IEndSessionHandler.cs new file mode 100644 index 00000000..c7af5d79 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/IEndSessionHandler.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.EndSession; + +public interface IEndSessionHandler +{ + Task HandleAsync(Server.Model.EndSessionRequest endSessionRequest); +} \ No newline at end of file diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionErrorResponse.cs new file mode 100644 index 00000000..b816589e --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionErrorResponse.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents an error response for ending a user's session. +/// +/// The error code describing the reason for the error. +/// A description providing additional information about the error. +/// An instance of EndSessionErrorResponse representing the error response. +public record EndSessionErrorResponse(string Error, string ErrorDescription) : EndSessionResponse; diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationError.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationError.cs new file mode 100644 index 00000000..8a9e4c27 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationError.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents a validation error for an end session request. +/// +/// The error code describing the validation error. +/// A description providing additional information about the validation error. +/// An instance of EndSessionRequestValidationError representing the validation error. +public record EndSessionRequestValidationError(string Error, string ErrorDescription) + : EndSessionRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationResult.cs new file mode 100644 index 00000000..0497e82d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionRequestValidationResult.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents the result of validating an end session request. +/// +public abstract record EndSessionRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionResponse.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionResponse.cs new file mode 100644 index 00000000..15ac9824 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents the response for ending a user's session. +/// +public abstract record EndSessionResponse; diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionSuccessfulResponse.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionSuccessfulResponse.cs new file mode 100644 index 00000000..49c0932c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/EndSessionSuccessfulResponse.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents a successful response for ending a user's session. +/// +public record EndSessionSuccessfulResponse(Uri? PostLogoutRedirectUri, IList FrontChannelLogoutRequestUris) + : EndSessionResponse +{ + /// + /// Gets or sets the URI to which the user should be redirected after successfully logging out (optional). + /// + public Uri? PostLogoutRedirectUri { get; init; } = PostLogoutRedirectUri; + + /// + /// Gets a list of front-channel logout request URIs to be initiated after logging out. + /// + public IList FrontChannelLogoutRequestUris { get; init; } = FrontChannelLogoutRequestUris; +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestProcessor.cs new file mode 100644 index 00000000..3b955d76 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestProcessor.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents the interface for processing end-session requests. +/// +public interface IEndSessionRequestProcessor +{ + /// + /// Processes the specified end-session request. + /// + /// The valid end-session request. + /// A task representing the asynchronous operation, returning the end-session response. + Task ProcessAsync(ValidEndSessionRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestValidator.cs new file mode 100644 index 00000000..d7ae8d8e --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/IEndSessionRequestValidator.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents the interface for validating end-session requests. +/// +public interface IEndSessionRequestValidator +{ + /// + /// Validates the specified end-session request. + /// + /// The end-session request to validate. + /// A task representing the asynchronous operation, returning the validation result. + Task ValidateAsync(EndSessionRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/ValidEndSessionRequest.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/ValidEndSessionRequest.cs new file mode 100644 index 00000000..99991dc6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Interfaces/ValidEndSessionRequest.cs @@ -0,0 +1,45 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +/// +/// Represents a valid end-session request with the associated client information. +/// +public record ValidEndSessionRequest(EndSessionRequest Model, ClientInfo? ClientInfo) + : EndSessionRequestValidationResult +{ + /// + /// The end-session request model. + /// + public EndSessionRequest Model { get; init; } = Model; + + /// + /// The client information associated with the request. + /// + public ClientInfo? ClientInfo { get; init; } = ClientInfo; +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ClientValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ClientValidator.cs new file mode 100644 index 00000000..6a0b411f --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ClientValidator.cs @@ -0,0 +1,79 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Validates the client associated with an end-session request. +/// +/// +/// This class checks if the client exists and is authorized for the end-session request. +/// If the client is not found, it returns an error indicating an unauthorized client. +/// +public class ClientValidator : IEndSessionContextValidator +{ + /// + /// Initializes a new instance of the ClientValidator class with a client info provider and a logger. + /// + /// The client info provider to retrieve client information. + /// The logger to be used for logging purposes. + public ClientValidator( + ILogger logger, + IClientInfoProvider clientInfoProvider) + { + _clientInfoProvider = clientInfoProvider; + _logger = logger; + } + + private readonly IClientInfoProvider _clientInfoProvider; + private readonly ILogger _logger; + + /// + /// Validates the client associated with an end-session request. + /// + /// The validation context containing client information. + /// An error if the validation fails, or null if the request is valid. + public async Task ValidateAsync(EndSessionValidationContext context) + { + if (!context.ClientId.HasValue()) + return null; + + var clientInfo = await _clientInfoProvider.TryFindClientAsync(context.ClientId).WithLicenseCheck(); + if (clientInfo == null) + { + _logger.LogWarning("The client with id {ClientId} was not found", context.ClientId); + return new EndSessionRequestValidationError( + ErrorCodes.UnauthorizedClient, + "The client is not authorized"); + } + + context.ClientInfo = clientInfo; + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ConfirmationValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ConfirmationValidator.cs new file mode 100644 index 00000000..84e47975 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/ConfirmationValidator.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Represents a validator for end-session requests requiring user confirmation. +/// +public class ConfirmationValidator: IEndSessionContextValidator +{ + /// + /// Validates the end-session request for confirmation. + /// + /// The end-session validation context. + /// A task representing the asynchronous operation. + /// The result is a validation error if confirmation is missing; otherwise, null. + public Task ValidateAsync(EndSessionValidationContext context) + => Task.FromResult(Validate(context)); + + private static EndSessionRequestValidationError? Validate(EndSessionValidationContext context) + { + var request = context.Request; + + if (!request.Confirmed && !request.IdTokenHint.HasValue()) + { + return new EndSessionRequestValidationError( + ErrorCodes.ConfirmationRequired, + "The request requires to be confirmed by user"); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionContextValidatorComposite.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionContextValidatorComposite.cs new file mode 100644 index 00000000..0cae3fa6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionContextValidatorComposite.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Represents a composite validator for end-session requests. +/// +public class EndSessionContextValidatorComposite : IEndSessionContextValidator +{ + /// + /// Initializes a new instance of this class with an array of validation steps. + /// + /// The array of end-session context validators to execute. + public EndSessionContextValidatorComposite(IEndSessionContextValidator[] validationSteps) + { + _validationSteps = validationSteps; + } + + private readonly IEndSessionContextValidator[] _validationSteps; + + /// + /// Validates the end-session request using a composite of multiple validators. + /// + /// The end-session validation context. + /// A task representing the asynchronous operation. + /// The result is a validation error if any validation step fails; otherwise, null. + public async Task ValidateAsync(EndSessionValidationContext context) + { + foreach (var validationStep in _validationSteps) + { + var error = await validationStep.ValidateAsync(context); + if (error != null) + return error; + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionValidationContext.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionValidationContext.cs new file mode 100644 index 00000000..59d532b4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/EndSessionValidationContext.cs @@ -0,0 +1,48 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Represents the context for validating an end-session request. +/// +public record EndSessionValidationContext(EndSessionRequest Request) +{ + /// + /// The request object to validate. + /// + public EndSessionRequest Request { get; set; } = Request; + + /// + /// The ClientId associated with the request. + /// + public string? ClientId { get; set; } = Request.ClientId; + + /// + /// The ClientInfo object containing information about the client. + /// + /// Thrown when attempting to get a null value. + public ClientInfo? ClientInfo { get; set; } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IEndSessionContextValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IEndSessionContextValidator.cs new file mode 100644 index 00000000..39338440 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IEndSessionContextValidator.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Defines the interface for validating end-session requests in an authentication system. +/// +public interface IEndSessionContextValidator +{ + /// + /// Validates an end-session request asynchronously. + /// + /// The end-session validation context. + /// + /// A task that represents the asynchronous validation operation. + /// Returns an EndSessionRequestValidationError if validation fails, or null if successful. + /// + Task ValidateAsync(EndSessionValidationContext context); +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IdTokenHintValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IdTokenHintValidator.cs new file mode 100644 index 00000000..abd102f7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/IdTokenHintValidator.cs @@ -0,0 +1,88 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Validates the ID token hint in the context of an end-session request. +/// This validator checks if the ID token hint provided in the request is valid and matches the expected audience (client). +/// +public class IdTokenHintValidator : IEndSessionContextValidator +{ + /// + /// Initializes a new instance of the class. + /// + /// The JWT validator used to validate ID tokens. + public IdTokenHintValidator(IAuthServiceJwtValidator jwtValidator) + { + _jwtValidator = jwtValidator; + } + + private readonly IAuthServiceJwtValidator _jwtValidator; + + /// + /// Validates the ID token hint. + /// + /// The end-session validation context. + /// An error if validation fails, null if successful. + public async Task ValidateAsync(EndSessionValidationContext context) + { + var request = context.Request; + + if (!request.IdTokenHint.HasValue()) + return null; + + var result = await _jwtValidator.ValidateAsync(request.IdTokenHint); + + switch (result) + { + case ValidJsonWebToken { Token.Payload.Audiences: var audiences }: + if (!request.ClientId.HasValue()) + { + context.ClientId = audiences.Single(); // TODO find what to do if there are multiple audiences??? + } + else if (!audiences.Contains(request.ClientId, StringComparer.Ordinal)) + { + return new EndSessionRequestValidationError(ErrorCodes.InvalidRequest, + "The id token hint contains token issued for the client other than specified"); + } + + break; + + case JwtValidationError: + return new EndSessionRequestValidationError(ErrorCodes.InvalidRequest, + "The id token hint contains invalid token"); + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/EndSession/Validation/PostLogoutRedirectUrisValidator.cs b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/PostLogoutRedirectUrisValidator.cs new file mode 100644 index 00000000..017b9d83 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/EndSession/Validation/PostLogoutRedirectUrisValidator.cs @@ -0,0 +1,85 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Features.UriValidation; +using Microsoft.Extensions.Logging; +using static Abblix.Oidc.Server.Model.EndSessionRequest; + +namespace Abblix.Oidc.Server.Endpoints.EndSession.Validation; + +/// +/// Validates the post-logout redirect URIs for an end-session request. +/// +public class PostLogoutRedirectUrisValidator : IEndSessionContextValidator +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger for capturing validation information. + public PostLogoutRedirectUrisValidator(ILogger logger) + { + _logger = logger; + } + + private readonly ILogger _logger; + + /// + /// Validates the end-session request asynchronously. + /// + /// The end-session validation context. + /// + /// A task that represents the asynchronous validation operation. + /// Returns an EndSessionRequestValidationError if validation fails, or null if successful. + /// + public Task ValidateAsync(EndSessionValidationContext context) + => Task.FromResult(Validate(context)); + + private EndSessionRequestValidationError? Validate(EndSessionValidationContext context) + { + var request = context.Request; + + var redirectUri = request.PostLogoutRedirectUri; + if (redirectUri == null) + return null; + + if (context.ClientInfo == null) + { + return new EndSessionRequestValidationError( + ErrorCodes.UnauthorizedClient, + $"Unable to determine a client from {Parameters.ClientId} or {Parameters.IdTokenHint}, but it is necessary to validate {Parameters.PostLogoutRedirectUri} value"); + } + + var uriValidator = UriValidatorFactory.Create(context.ClientInfo.PostLogoutRedirectUris); + if (uriValidator.IsValid(redirectUri)) + return null; + + _logger.LogWarning("The post-logout redirect URI {RedirectUri} is invalid for client with id {ClientId}", + redirectUri, + context.ClientInfo.ClientId); + + return new EndSessionRequestValidationError( + ErrorCodes.InvalidRequest, + "The post-logout redirect URI is not valid for specified client"); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionHandler.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionHandler.cs new file mode 100644 index 00000000..9553cbfb --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionHandler.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Defines the contract for handling introspection requests to determine the current state and validity of +/// OAuth 2.0 tokens, such as access tokens or refresh tokens. +/// +public interface IIntrospectionHandler +{ + /// + /// Asynchronously processes an introspection request, validating its authorization and the token in question, + /// and then returning the token's state and other relevant information. + /// + /// The introspection request containing the token and possibly other parameters + /// required for validating the request and introspecting the token. + /// Additional information about the client making the request, which may be necessary + /// for validating the request in certain contexts. + /// + /// A that, when completed successfully, results in an . + /// This response contains information about the token's current state, such as whether it is active, + /// and potentially other metadata. In case of an invalid request, + /// the response will detail the reasons for rejection. + /// + /// + /// Implementations of this interface play a critical role in the security of OAuth 2.0 and OIDC systems + /// by enabling resource servers and other relying parties to verify the validity and metadata of tokens. + /// This helps prevent unauthorized access and ensures that tokens are used in accordance with their + /// intended scopes and lifetimes. + /// + Task HandleAsync( + IntrospectionRequest introspectionRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestProcessor.cs new file mode 100644 index 00000000..33146b4d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestProcessor.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents a contract for generating a document with metadata about an inspected token, including its active status. +/// +public interface IIntrospectionRequestProcessor +{ + /// + /// Processes an introspection request and returns an IntrospectionResponse with token metadata. + /// + /// The ValidIntrospectionRequest containing the token to be inspected. + /// An IntrospectionResponse containing metadata about the token. + Task ProcessAsync(ValidIntrospectionRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestValidator.cs new file mode 100644 index 00000000..4461aec2 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IIntrospectionRequestValidator.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents a contract for validating introspection request properties and authenticating the initiating client. +/// +public interface IIntrospectionRequestValidator +{ + /// + /// Validates the provided introspection request and authenticates the client. + /// + /// The IntrospectionRequest to be validated. + /// Additional client request information for contextual validation. + /// An IntrospectionRequestValidationResult indicating the result of the validation. + Task ValidateAsync( + IntrospectionRequest introspectionRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionErrorResponse.cs new file mode 100644 index 00000000..4a4f58ab --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionErrorResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents an error response for introspection requests, including error code and description. +/// +public record IntrospectionErrorResponse(string Error, string ErrorDescription) : IntrospectionResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationError.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationError.cs new file mode 100644 index 00000000..142ee222 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationError.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents a validation error response for introspection requests, including error code and description. +/// +public record IntrospectionRequestValidationError(string Error, string ErrorDescription) + : IntrospectionRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationResult.cs new file mode 100644 index 00000000..4f22b24d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionRequestValidationResult.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents the result of introspection request validation. This is an abstract base class for validation results. +/// +public abstract record IntrospectionRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionResponse.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionResponse.cs new file mode 100644 index 00000000..b590eb2d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents the response from an introspection request, providing information about the inspected token's metadata. +/// +public abstract record IntrospectionResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionSuccessResponse.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionSuccessResponse.cs new file mode 100644 index 00000000..003d227d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/IntrospectionSuccessResponse.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents a successful introspection response, indicating whether the token is active and providing its claims. +/// +/// +/// Specific implementations may extend this structure with their own service-specific response names as +/// top-level members of this JSON object. Response names intended for use across domains must be registered +/// in the "OAuth Token Introspection Response" registry as defined in Section 3.1. +/// +public record IntrospectionSuccessResponse(bool Active, JsonObject? Claims) : IntrospectionResponse +{ + /// + /// Gets or sets whether the token is active. + /// + public bool Active { get; } = Active; + + /// + /// Gets or sets the claims associated with the token. + /// + public JsonObject? Claims { get; } = Claims; +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/ValidIntrospectionRequest.cs b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/ValidIntrospectionRequest.cs new file mode 100644 index 00000000..548e3a37 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/Interfaces/ValidIntrospectionRequest.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + +/// +/// Represents a valid introspection request result. +/// +public record ValidIntrospectionRequest : IntrospectionRequestValidationResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The introspection request model. + /// The JSON Web Token to introspect. + public ValidIntrospectionRequest(IntrospectionRequest model, JsonWebToken token) + { + Model = model; + Token = token; + } + + /// + /// Initializes a new instance of the class when the token is not provided. + /// + /// The introspection request model. + private ValidIntrospectionRequest(IntrospectionRequest model) + { + Model = model; + Token = null; + } + + /// + /// Creates a valid introspection request for an invalid token. + /// + /// The introspection request model. + /// A valid introspection request with the "active" field set to "false." + /// + /// See https://www.rfc-editor.org/rfc/rfc7662#section-5.2 + /// + public static ValidIntrospectionRequest InvalidToken(IntrospectionRequest model) + { + // Note that to avoid disclosing too much of the authorization server's state to a third party, the authorization server + // SHOULD NOT include any additional information about an inactive token, including why the token is inactive. + + // That is why we do not return the token here even if it is valid, but for example it was issued for another client. + return new(model); + } + + /// + /// The introspection request model. + /// + public IntrospectionRequest Model { get; } + + /// + /// The JSON Web Token to introspect. + /// + public JsonWebToken? Token { get; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionHandler.cs b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionHandler.cs new file mode 100644 index 00000000..1d74d451 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionHandler.cs @@ -0,0 +1,87 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Introspection; + +/// +/// Manages the processing of token introspection requests according to OAuth 2.0 specifications, facilitating +/// the validation and introspection of tokens to determine their current state and metadata. +/// +public class IntrospectionHandler : IIntrospectionHandler +{ + /// + /// Initializes a new instance of the class, equipping it with the necessary + /// components to validate and process introspection requests. + /// + /// An implementation of tasked with + /// validating introspection requests against OAuth 2.0 standards. + /// An implementation of responsible + /// for processing validated introspection requests and retrieving token information. + public IntrospectionHandler( + IIntrospectionRequestValidator validator, + IIntrospectionRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IIntrospectionRequestValidator _validator; + private readonly IIntrospectionRequestProcessor _processor; + + /// + /// Asynchronously handles an introspection request by validating the request and, if valid, processing it to + /// return the state and metadata of the specified token. + /// + /// The introspection request containing the token to be introspected and + /// other relevant parameters. + /// Supplementary information about the client making the request, + /// useful for contextual validation. + /// + /// A that resolves to an , which includes the token's + /// active status and potentially other metadata, or an error response if the request is invalid. + /// + /// + /// Implementations of this method are crucial for maintaining the integrity and security of token-based + /// authentication systems by allowing resource servers and other entities to verify the validity + /// and attributes of tokens. + /// + public async Task HandleAsync( + IntrospectionRequest introspectionRequest, + ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(introspectionRequest, clientRequest); + + return validationResult switch + { + ValidIntrospectionRequest validRequest => await _processor.ProcessAsync(validRequest), + + IntrospectionRequestValidationError { Error: var error, ErrorDescription: var description } + => new IntrospectionErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestProcessor.cs new file mode 100644 index 00000000..47f2ab8b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestProcessor.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; + + + +namespace Abblix.Oidc.Server.Endpoints.Introspection; + +/// +/// Implements the logic for processing introspection requests and generating introspection responses. +/// +/// +/// This class handles the introspection of tokens to determine if they are active or inactive. +/// It follows the OAuth 2.0 Token Introspection specification (RFC 7662). +/// The processor examines the token's status and provides an appropriate response as per the specification. +/// +public class IntrospectionRequestProcessor : IIntrospectionRequestProcessor +{ + /// + /// Processes an introspection request and returns the corresponding introspection response. + /// + /// The valid introspection request to process. It contains the token to be introspected. + /// + /// A representing the asynchronous operation, with a result of . + /// The response indicates the active status of the token and contains associated claims. + /// + public Task ProcessAsync(ValidIntrospectionRequest request) => Task.FromResult(Process(request)); + + private static IntrospectionResponse Process(ValidIntrospectionRequest request) + { + if (request.Token == null) + { + // https://www.rfc-editor.org/rfc/rfc7662#section-2.2: + + // If the introspection call is properly authorized but the token is not active, does not exist on this server, + // or the protected resource is not allowed to introspect this particular token, then the authorization server + // MUST return an introspection response with the "active" field set to "false". + + // Note that to avoid disclosing too much of the authorization server's state to a third party, the authorization server + // SHOULD NOT include any additional information about an inactive token, including why the token is inactive. + return new IntrospectionSuccessResponse(false, null); + } + + // The authorization server MAY respond differently to different protected resources making the same request. + // For instance, an authorization server MAY limit which scopes from a given token are returned for each protected resource + // to prevent a protected resource from learning more about the larger network than is necessary for its operation. + + return new IntrospectionSuccessResponse(true, request.Token.Payload.Json); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestValidator.cs new file mode 100644 index 00000000..e894589a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Introspection/IntrospectionRequestValidator.cs @@ -0,0 +1,107 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; + + +namespace Abblix.Oidc.Server.Endpoints.Introspection; + +/// +/// Validates the introspection request properties and authenticates a client that initiated the request. +/// +/// +/// This class performs validation of introspection requests and client authentication. +/// It ensures that the request is authorized and the provided token is valid for the client. +/// The validation process includes checking the authenticity of the client and the integrity of the token. +/// It leverages a client request authenticator for client authentication and a JWT validator for token validation. +/// +public class IntrospectionRequestValidator : IIntrospectionRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger for logging activities within the validator. + /// The client request authenticator to authenticate the client. + /// The JWT validator to validate the token. + public IntrospectionRequestValidator( + ILogger logger, + IClientAuthenticator clientAuthenticator, + IAuthServiceJwtValidator jwtValidator) + { + _logger = logger; + _clientAuthenticator = clientAuthenticator; + _jwtValidator = jwtValidator; + } + + private readonly ILogger _logger; + private readonly IClientAuthenticator _clientAuthenticator; + private readonly IAuthServiceJwtValidator _jwtValidator; + + /// + /// Validates the introspection request properties and authenticates a client that initiated the request. + /// + /// The introspection request to validate. It includes the token and client information for validation. + /// Additional client request information for contextual validation. + /// + /// A task representing the asynchronous validation operation. The task result contains the + /// which indicates whether the request is valid or contains errors. + /// + public async Task ValidateAsync( + IntrospectionRequest introspectionRequest, + ClientRequest clientRequest) + { + var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest); + if (clientInfo == null) + { + return new IntrospectionRequestValidationError(ErrorCodes.InvalidClient, "The client is not authorized"); + } + + var result = await _jwtValidator.ValidateAsync(introspectionRequest.Token); + switch (result) + { + case ValidJsonWebToken { Token: var token }: + + var clientId = token.Payload.ClientId; + if (clientId != clientInfo.ClientId) + { + // The token was issued to another client + return ValidIntrospectionRequest.InvalidToken(introspectionRequest); + } + + return new ValidIntrospectionRequest(introspectionRequest, token); + + case JwtValidationError error: + _logger.LogWarning("The incoming JWT token is invalid: {@JwtValidationError}", error); + return ValidIntrospectionRequest.InvalidToken(introspectionRequest); + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + } +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationHandler.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationHandler.cs new file mode 100644 index 00000000..f36f4a1b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationHandler.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; + +/// +/// Defines the contract for handling Pushed Authorization Requests (PAR) as specified in OAuth 2.0 and OpenID Connect. +/// Ensures that implementations can validate and process these requests in a secure and compliant manner. +/// +public interface IPushedAuthorizationHandler +{ + /// + /// Asynchronously handles and processes a Pushed Authorization Request, ensuring it complies with OAuth 2.0 + /// and OpenID Connect specifications. + /// + /// An instance of representing the details + /// of the authorization request submitted by the client. + /// An instance of providing additional information about + /// the client making the request, used for contextual validation. + /// + /// A that resolves to an , indicating the outcome of the + /// request processing. The response can be a successful authorization or an error response if the request + /// fails validation or processing. + /// + /// + /// This method is central to the PAR mechanism, enabling clients to pre-register authorization requests. + /// It validates the request against system policies and, if valid, processes it to generate a unique request URI + /// or returns an error if the request is invalid or unauthorized. This approach enhances security by minimizing + /// the exposure of sensitive information in subsequent authorization requests. + /// + Task HandleAsync( + AuthorizationRequest authorizationRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestProcessor.cs new file mode 100644 index 00000000..b2396052 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestProcessor.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; + +/// +/// Processes valid pushed authorization requests, generating a response that includes +/// the request's URI and its expiration. +/// +public interface IPushedAuthorizationRequestProcessor +{ + /// + /// Asynchronously processes a valid authorization request and generates a response. + /// + /// The valid authorization request to process. + /// A task that resolves to an authorization response, including the + /// request URI and expiration time. + Task ProcessAsync(ValidAuthorizationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestValidator.cs new file mode 100644 index 00000000..4cca60e9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/IPushedAuthorizationRequestValidator.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; + +/// +/// Provides validation for pushed authorization requests in an OAuth 2.0 context, ensuring they adhere +/// to protocol specifications. +/// This interface evaluates the conformity of authorization requests with expected parameters and +/// limitations before their acceptance for processing. +/// +public interface IPushedAuthorizationRequestValidator +{ + /// + /// Asynchronously validates a pushed authorization request against OAuth 2.0 specifications. + /// This method ensures the request meets all necessary criteria and constraints defined for secure processing. + /// + /// The authorization request to be validated. + /// Additional client request information for contextual validation. + /// A task that upon completion provides a validation result, indicating either success and validity + /// of the request or the presence of errors. + Task ValidateAsync( + AuthorizationRequest authorizationRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/PushedAuthorizationResponse.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/PushedAuthorizationResponse.cs new file mode 100644 index 00000000..67cd9a81 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/Interfaces/PushedAuthorizationResponse.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; +using AuthorizationResponse = Abblix.Oidc.Server.Endpoints.Authorization.Interfaces.AuthorizationResponse; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; + +/// +/// Represents the response to a pushed authorization request. This response includes the URI +/// where the authorization request is stored and the duration for which the request will remain valid. +/// +public record PushedAuthorizationResponse(AuthorizationRequest Model, Uri RequestUri, TimeSpan ExpiresIn) + : AuthorizationResponse(Model) +{ + /// + /// The URI where the authorization request is stored. + /// This URI is used by the client to refer to the authorization request in subsequent operations. + /// + public Uri RequestUri { get; init; } = RequestUri; + + /// + /// The duration for which the authorization request is considered valid. + /// After this period, the request may no longer be retrievable or usable. + /// + public TimeSpan ExpiresIn { get; init; } = ExpiresIn; +}; diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationHandler.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationHandler.cs new file mode 100644 index 00000000..ff6be883 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationHandler.cs @@ -0,0 +1,108 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization; + +/// +/// Handles the processing of Pushed Authorization Requests (PAR) by validating the requests and then processing +/// them if valid. This class acts as an intermediary between the validation and processing stages of the PAR workflow. +/// +public class PushedAuthorizationHandler : IPushedAuthorizationHandler +{ + /// + /// Initializes a new instance of the class with specified validator + /// and processor services. + /// + /// An instance of used for validating + /// pushed authorization requests. + /// An instance of used for processing + /// validated authorization requests. + public PushedAuthorizationHandler( + IAuthorizationRequestFetcher fetcher, + IPushedAuthorizationRequestValidator validator, + IPushedAuthorizationRequestProcessor processor) + { + _fetcher = fetcher; + _validator = validator; + _processor = processor; + } + + private readonly IAuthorizationRequestFetcher _fetcher; + private readonly IPushedAuthorizationRequestValidator _validator; + private readonly IPushedAuthorizationRequestProcessor _processor; + + /// + /// Asynchronously handles a pushed authorization request by first validating it and then processing it if + /// the validation is successful. + /// + /// The authorization request details as received from the client. + /// Additional client request information that may be needed for contextual validation. + /// + /// + /// A that upon completion yields an , which could be a + /// successful response with the request being processed or an error response if the validation fails. + /// + /// + /// This method ensures that pushed authorization requests are thoroughly validated against the system's + /// criteria before proceeding with processing. This validation includes, but is not limited to, verifying + /// the client's identity, the request's integrity, and its compliance with the system's policies. + /// Successful validation leads to the processing of the request, which typically involves generating a request URI + /// or an error response in case of failure. + /// + public async Task HandleAsync( + AuthorizationRequest authorizationRequest, + ClientRequest clientRequest) + { + var fetchResult = await _fetcher.FetchAsync(authorizationRequest); + switch (fetchResult) + { + case FetchResult.Success success: + authorizationRequest = success.Request; + break; + + case FetchResult.Fault { Error: var error }: + return new AuthorizationError(authorizationRequest, error); + } + + var validationResult = await _validator.ValidateAsync(authorizationRequest, clientRequest); + + return validationResult switch + { + ValidAuthorizationRequest validRequest => await _processor.ProcessAsync(validRequest), + + AuthorizationRequestValidationError error => new AuthorizationError( + authorizationRequest, + error.Error, + error.ErrorDescription, + error.ResponseMode, + error.RedirectUri), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestProcessor.cs new file mode 100644 index 00000000..4bb80af7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestProcessor.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Features.Storages; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization; + +/// +/// Processes pushed authorization requests by storing them and generating a response +/// that includes the request URI and expiration information. +/// +public class PushedAuthorizationRequestProcessor : IPushedAuthorizationRequestProcessor +{ + /// + /// Initializes a new instance of the class + /// with a specified authorization request storage. + /// + /// The storage used to keep the authorization requests. + /// + public PushedAuthorizationRequestProcessor( + IAuthorizationRequestStorage storage, + IOptionsSnapshot options) + { + _storage = storage; + _options = options; + } + + private readonly IAuthorizationRequestStorage _storage; + private readonly IOptionsSnapshot _options; + + /// + /// Asynchronously processes a valid pushed authorization request by storing it and returning a response + /// that includes the request URI for later retrieval and the duration for which the request is valid. + /// + /// The valid pushed authorization request to process. + /// A task that resolves to an containing + /// the request URI and expiration information. + public async Task ProcessAsync(ValidAuthorizationRequest request) + => await _storage.StoreAsync(request.Model,_options.Value.PushedAuthorizationRequestExpiresIn); +} diff --git a/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestValidator.cs new file mode 100644 index 00000000..89ea2695 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/PushedAuthorization/PushedAuthorizationRequestValidator.cs @@ -0,0 +1,106 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Model; +using static Abblix.Oidc.Server.Model.AuthorizationRequest.Parameters; + +namespace Abblix.Oidc.Server.Endpoints.PushedAuthorization; + +/// +/// Validates pushed authorization requests by enforcing OAuth 2.0 protocol constraints. +/// This validator ensures that requests do not use prohibited parameters and +/// comply with standard authorization request requirements. +/// +public class PushedAuthorizationRequestValidator : IPushedAuthorizationRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// This validator is responsible for validating pushed authorization requests according to the OAuth 2.0 protocol. + /// It ensures that the requests adhere to the required standards and do not include any prohibited parameters. + /// + /// + /// The used to validate the standard parameters of authorization + /// requests. + /// + /// + /// The used to authenticate the client making the pushed authorization request. + /// + public PushedAuthorizationRequestValidator( + IAuthorizationRequestValidator authorizationRequestValidator, + IClientAuthenticator clientAuthenticator) + { + _authorizationRequestValidator = authorizationRequestValidator; + _clientAuthenticator = clientAuthenticator; + } + + private readonly IAuthorizationRequestValidator _authorizationRequestValidator; + private readonly IClientAuthenticator _clientAuthenticator; + + /// + /// Validates a pushed authorization request according to OAuth 2.0 and OpenID Connect standards. + /// This method ensures that the request does not contain prohibited parameters like 'request_uri' and + /// verifies that it adheres to the client's registered parameters. It effectively prevents misuse + /// and ensures that the request is legitimately associated with the authenticated client. + /// + /// The authorization request to be validated. + /// + /// A task that resolves to a validation result, indicating whether the request is valid + /// and adheres to the expected protocol constraints. + public async Task ValidateAsync( + AuthorizationRequest authorizationRequest, + ClientRequest clientRequest) + { + var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest); + if (clientInfo == null) + { + return ErrorFactory.InvalidClient("The client is not authorized"); + } + + if (authorizationRequest.RequestUri is not null) + { + return ErrorFactory.InvalidRequestUri( + $"{RequestUri} is prohibited to use in pushed authorization request"); + } + + var result = await _authorizationRequestValidator.ValidateAsync(authorizationRequest); + + if (result is ValidAuthorizationRequest { + Model: { ClientId: var clientId, RedirectUri: var redirectUri }, + ResponseMode: var responseMode + } && clientInfo.ClientId != clientId) + { + return new AuthorizationRequestValidationError( + + ErrorCodes.InvalidRequest, + "The pushed authorization request must be bound to the client that posted it", + redirectUri, + responseMode); + } + + return result; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationHandler.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationHandler.cs new file mode 100644 index 00000000..b7d1e73a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationHandler.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Defines a contract for handling revocation requests for access or refresh tokens as per OAuth 2.0 Token Revocation +/// specifications. Ensures implementations can securely validate and process such requests to revoke tokens effectively. +/// +public interface IRevocationHandler +{ + /// + /// Asynchronously handles a token revocation request by validating and then processing it to revoke + /// the specified token. + /// + /// The details of the revocation request, including the token to be revoked. + /// Additional information about the client making the revocation request, + /// necessary for context-specific validation. + /// + /// A that resolves to a , indicating the outcome of + /// the revocation process. This could be a successful acknowledgment of the revocation or an error response + /// if the request fails validation or processing. + /// + /// + /// This method is crucial for maintaining the security and integrity of the authorization server by allowing + /// clients to revoke tokens that are no longer needed or may have been compromised. + /// Implementations must ensure that revocation requests are authenticated and authorized before proceeding + /// with token revocation, adhering to the OAuth 2.0 Token Revocation specification (RFC 7009). + /// + Task HandleAsync( + RevocationRequest revocationRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestProcessor.cs new file mode 100644 index 00000000..21a0f5b6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestProcessor.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents the capability to handle token revocation requests. +/// The authorization server invalidates tokens immediately upon revocation, preventing their future use. +/// Depending on the server's policy, revoking a token may also affect related tokens and the underlying authorization grant. +/// If a refresh token is revoked and the server supports revocation of access tokens, associated access tokens should also be invalidated. +/// +/// +/// For more details, refer to RFC 7009 Section 2.1: https://www.rfc-editor.org/rfc/rfc7009#section-2.1 +/// +public interface IRevocationRequestProcessor +{ + /// + /// Processes a token revocation request. + /// This method is responsible for handling the request to revoke a token, ensuring that the token and any associated tokens are invalidated. + /// + /// The valid revocation request to be processed. It contains the token that needs to be revoked along with any relevant information. + /// A task representing the asynchronous operation, which upon completion will return a indicating the outcome of the revocation process. + Task ProcessAsync(ValidRevocationRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestValidator.cs new file mode 100644 index 00000000..bd80565c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/IRevocationRequestValidator.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents the capability to validate revocation requests. +/// The authorization server validates client credentials (for confidential clients) and checks if the token was issued +/// to the requesting client. If validation fails, the request is refused, and an error message is provided to +/// the client by the authorization server. +/// +public interface IRevocationRequestValidator +{ + /// + /// Validates a revocation request. + /// + /// The revocation request to be validated. + /// Additional client request information for contextual validation. + /// A task representing the asynchronous operation with the validation result. + Task ValidateAsync( + RevocationRequest revocationRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationErrorResponse.cs new file mode 100644 index 00000000..0ee7a141 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationErrorResponse.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents an error response for token revocation. +/// Contains error and error description information to be sent to the client. +/// +public record RevocationErrorResponse(string Error, string ErrorDescription) : RevocationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationError.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationError.cs new file mode 100644 index 00000000..415aa831 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationError.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents a validation error response for token revocation requests. +/// Contains error and error description information for informing the client about the validation error. +/// +public record RevocationRequestValidationError(string Error, string ErrorDescription) + : RevocationRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationResult.cs new file mode 100644 index 00000000..8585e1fa --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationRequestValidationResult.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents the result of validating a token revocation request. +/// This is an abstract base class for various validation results. +/// +public abstract record RevocationRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationResponse.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationResponse.cs new file mode 100644 index 00000000..a3b6f5dd --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/RevocationResponse.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents the response of a token revocation request. +/// This is an abstract base class for various revocation responses. +/// +public abstract record RevocationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/TokenRevokedResponse.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/TokenRevokedResponse.cs new file mode 100644 index 00000000..edac2b4c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/TokenRevokedResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents a response indicating that the token has been successfully revoked. +/// +public record TokenRevokedResponse : RevocationResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/ValidRevocationRequest.cs b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/ValidRevocationRequest.cs new file mode 100644 index 00000000..aa4fc239 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/Interfaces/ValidRevocationRequest.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; + +/// +/// Represents a valid revocation request, including the request model and the associated token, if available. +/// +public record ValidRevocationRequest : RevocationRequestValidationResult +{ + /// + /// Initializes a valid revocation request with the provided model and token. + /// + public ValidRevocationRequest(RevocationRequest model, JsonWebToken token) + { + Model = model; + Token = token; + } + + /// + /// Creates a valid revocation request for an invalid token without a token association. + /// + /// + /// Invalid tokens do not cause an error response since the client cannot handle such an error in a reasonable way. + /// Moreover, the purpose of the revocation request, invalidating the particular token, is already achieved. + /// See https://www.rfc-editor.org/rfc/rfc7009#section-2.2 + /// + public static ValidRevocationRequest InvalidToken(RevocationRequest model) => new(model); + + /// + private ValidRevocationRequest(RevocationRequest model) + { + Model = model; + Token = null; + } + + /// + /// The revocation request model. + /// + public RevocationRequest Model { get; } + + /// + /// The associated token, if available. + /// + public JsonWebToken? Token { get; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/RevocationHandler.cs b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationHandler.cs new file mode 100644 index 00000000..c186b6a6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationHandler.cs @@ -0,0 +1,92 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Revocation; + +/// +/// Manages the handling of token revocation requests in accordance with OAuth 2.0 specifications, ensuring that such +/// requests are properly validated and processed to revoke tokens as intended. +/// +public class RevocationHandler : IRevocationHandler +{ + /// + /// Initializes a new instance of the class with the necessary validator and + /// processor for revocation requests. + /// + /// + /// An implementation of responsible for validating the revocation request + /// against the OAuth 2.0 specifications. + /// + /// An implementation of responsible for processing validated revocation + /// requests to effectively revoke tokens. + public RevocationHandler( + IRevocationRequestValidator validator, + IRevocationRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IRevocationRequestValidator _validator; + private readonly IRevocationRequestProcessor _processor; + + /// + /// Asynchronously handles a token revocation request by validating it and then processing it if the + /// validation succeeds. + /// + /// + /// The revocation request details, mapped to the model expected by the system. + /// + /// Additional client request information that may be necessary for validation. + /// + /// A that resolves to a , indicating the outcome of + /// the request handling. This can either be a successful revocation or an error response if + /// the request does not pass validation. + /// + /// + /// This method plays a critical role in maintaining the security and integrity of the OAuth 2.0 ecosystem + /// by allowing tokens to be revoked when they are no longer needed or when a security issue necessitates + /// their invalidation. It ensures that revocation requests are thoroughly vetted before any action is taken, + /// preventing unauthorized or malicious attempts to revoke tokens. + /// + public async Task HandleAsync( + RevocationRequest revocationRequest, + ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(revocationRequest, clientRequest); + + var response = validationResult switch + { + ValidRevocationRequest validRequest => await _processor.ProcessAsync(validRequest), + + RevocationRequestValidationError { Error: var error, ErrorDescription: var description } + => new RevocationErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestProcessor.cs new file mode 100644 index 00000000..ba8934de --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestProcessor.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens.Revocation; + + +namespace Abblix.Oidc.Server.Endpoints.Revocation; + +/// +/// Processes revocation requests for tokens. +/// This class is responsible for handling the logic associated with revoking tokens, such as access tokens or refresh tokens. +/// +public class RevocationRequestProcessor : IRevocationRequestProcessor +{ + /// + /// Initializes a new instance of the class. + /// Sets up the processor with a token registry which will be used for updating the status of the tokens. + /// + /// The token registry to be used by this processor for managing token statuses. + public RevocationRequestProcessor(ITokenRegistry tokenRegistry) + { + _tokenRegistry = tokenRegistry; + } + + private readonly ITokenRegistry _tokenRegistry; + + /// + /// Asynchronously processes a valid revocation request. + /// This method handles the revocation of a specified token by changing its status to 'Revoked' in the token registry. + /// The operation ensures that the token is no longer valid for any future requests. + /// + /// The revocation request to be processed. Contains information about the token to be revoked. + /// + /// A representing the asynchronous operation, which upon completion will yield a . + /// The response signifies the outcome of the revocation process. + /// + public async Task ProcessAsync(ValidRevocationRequest request) + { + var payload = request.Token?.Payload; + if (payload is { JwtId: {} jwtId, ExpiresAt: {} expiresAt }) + await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Revoked, expiresAt); + + return new TokenRevokedResponse(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs new file mode 100644 index 00000000..9919531d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs @@ -0,0 +1,102 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Revocation; + +/// +/// Validates revocation requests in accordance with OAuth 2.0 standards. +/// This class is responsible for ensuring that revocation requests meet the criteria specified in +/// OAuth 2.0 Token Revocation (RFC 7009). It validates the authenticity of the client and the token involved in the request. +/// +public class RevocationRequestValidator : IRevocationRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// The constructor sets up the validator with necessary components for client authentication and JWT validation. + /// + /// The client request authenticator to be used in the validation process. + /// The JWT validator to be used for validating the token included in the revocation request. + public RevocationRequestValidator( + IClientAuthenticator clientAuthenticator, + IAuthServiceJwtValidator jwtValidator) + { + _clientAuthenticator = clientAuthenticator; + _jwtValidator = jwtValidator; + } + + private readonly IClientAuthenticator _clientAuthenticator; + private readonly IAuthServiceJwtValidator _jwtValidator; + + /// + /// Asynchronously validates a revocation request against the OAuth 2.0 revocation request specifications. + /// It checks the client's credentials and the validity of the token to be revoked. The validation ensures + /// that the token belongs to the authenticated client and is valid as per JWT standards. + /// + /// The revocation request to be validated. Contains the token to be revoked and client information. + /// Additional client request information for contextual validation. + /// + /// A representing the asynchronous operation, which upon completion will yield a + /// . + /// The result indicates whether the request is valid or contains any errors. + /// + public async Task ValidateAsync( + RevocationRequest revocationRequest, + ClientRequest clientRequest) + { + var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest); + if (clientInfo == null) + { + return new RevocationRequestValidationError(ErrorCodes.InvalidClient, "The client is not authorized"); + } + + var result = await _jwtValidator.ValidateAsync(revocationRequest.Token); + switch (result) + { + case ValidJsonWebToken { Token: var token }: + + var clientId = token.Payload.ClientId; + if (clientId != clientInfo.ClientId) + { + //TODO maybe log the message: The token was issued to another client? + return ValidRevocationRequest.InvalidToken(revocationRequest); + } + + return new ValidRevocationRequest(revocationRequest, token); + + case JwtValidationError error: //TODO log error + return ValidRevocationRequest.InvalidToken(revocationRequest); + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + } +} diff --git a/Abblix.Oidc.Server/Endpoints/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/Endpoints/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..f578f45d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/ServiceCollectionExtensions.cs @@ -0,0 +1,339 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.DependencyInjection; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Implementation; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.RequestFetching; +using Abblix.Oidc.Server.Endpoints.Authorization.Validation; +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication; +using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces; +using Abblix.Oidc.Server.Endpoints.CheckSession; +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation; +using Abblix.Oidc.Server.Endpoints.EndSession; +using Abblix.Oidc.Server.Endpoints.EndSession.Interfaces; +using Abblix.Oidc.Server.Endpoints.EndSession.Validation; +using Abblix.Oidc.Server.Endpoints.Introspection; +using Abblix.Oidc.Server.Endpoints.Introspection.Interfaces; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization; +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Endpoints.Revocation; +using Abblix.Oidc.Server.Endpoints.Revocation.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token; +using Abblix.Oidc.Server.Endpoints.Token.Grants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Endpoints.UserInfo; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using ClientValidator = Abblix.Oidc.Server.Endpoints.Authorization.Validation.ClientValidator; +using PostLogoutRedirectUrisValidator = Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Validation.PostLogoutRedirectUrisValidator; + +namespace Abblix.Oidc.Server.Endpoints; + +public static class ServiceCollectionExtensions +{ + /// + /// Adds services and processors for handling authorization requests to the service collection. + /// + /// + /// This setup is crucial for supporting the OAuth 2.0 and OpenID Connect authorization flow, + /// ensuring that incoming authorization requests are correctly validated and processed. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddAuthorizationEndpoint(this IServiceCollection services) + { + return services + .AddAuthorizationRequestFetchers() + .AddAuthorizationContextValidators() + .AddScoped() + .AddScoped() + .AddScoped(); + } + + public static IServiceCollection AddAuthorizationRequestFetchers(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Compose(); + } + + /// + /// Adds a series of validators for authorization context as a composite service to ensure comprehensive validation + /// of authorization requests. + /// + /// + /// This method composes a pipeline of validators for various aspects of the authorization context, + /// such as request object validation, client validation, and more. + /// This composite validator approach enables modular and extensible validation logic, ensuring that + /// authorization requests meet all necessary criteria and standards. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddAuthorizationContextValidators(this IServiceCollection services) + { + return services + // compose AuthorizationContext validation as a pipeline of several IAuthorizationContextValidator + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Compose(); + } + + /// + /// Registers validators and processors for pushed authorization requests (PAR), enhancing the security and + /// efficiency of the authorization process by allowing clients to send requests directly to + /// the authorization server via a back-channel connection. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddPushedAuthorizationEndpoint(this IServiceCollection services) + { + return services + .AddScoped() + .AddScoped( + Dependency.Override()) + .AddScoped(); + } + + /// + /// Adds services for validating and processing token requests according to OAuth 2.0 and OpenID Connect + /// standards. This setup supports various grant types, ensuring that token requests are handled securely and + /// efficiently, facilitating the issuance of access tokens, refresh tokens, and ID tokens to clients. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddTokenEndpoint(this IServiceCollection services) + { + return services + .AddAuthorizationGrants() + + .AddScoped() + .AddScoped() + .AddScoped() + .Decorate(); + } + + /// + /// Enables support for the password grant type, acknowledging its security considerations. + /// + /// + /// This method is intentionally separated from the standard OIDC core service registration due to the inherent + /// security risks associated with the password grant type. The password grant type requires the client to handle + /// user credentials directly, which can increase the risk of credential exposure and related security issues. + /// By isolating this method, we ensure that developers make a deliberate decision to enable this feature, being + /// fully aware of its security implications. It's recommended to use more secure grant types like authorization + /// code or client credentials whenever possible. + /// + /// The to add the password grant handler to. + /// The so additional calls can be chained. + public static IServiceCollection EnablePasswordGrant(this IServiceCollection services) + { + return services + .AddSingleton(); + } + + /// + /// Adds services for validating and processing revocation requests. This capability is essential for OAuth 2.0 + /// compliance, enabling clients to revoke access or refresh tokens when they are no longer needed or + /// if a security issue arises, thus minimizing the potential for unauthorized use of tokens. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddAuthorizationGrants(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton() + .Compose(); + } + + /// + /// Adds services for validating and processing revocation requests. This capability is essential for OAuth 2.0 + /// compliance, enabling clients to revoke access or refresh tokens when they are no longer needed or if + /// a security issue arises, thus minimizing the potential for unauthorized use of tokens. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddRevocationEndpoint(this IServiceCollection services) + { + return services + .AddScoped() + .AddScoped() + .AddScoped(); + } + + /// + /// Adds validators and processors for introspection requests, enabling resource servers to verify the active status + /// of tokens and access token metadata. This feature is crucial for applications that need to validate tokens + /// coming from different clients or issued by external authorization servers. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddIntrospectionEndpoint(this IServiceCollection services) + { + return services + .AddScoped() + .AddScoped() + .AddScoped(); + } + + /// + /// Registers services for handling check session requests, facilitating session management in compliance with + /// OpenID Connect session management standards. + /// + /// + /// Adds a scoped service for processing check session requests, allowing clients to query the authentication status + /// of the user in an iframe. This is part of the OpenID Connect session management specification, + /// enabling applications to maintain a consistent user session state across different clients and + /// the identity provider. It supports functionalities for clients to detect when a user's session has ended + /// at the identity provider, prompting for re-authentication or logout as necessary. + /// + /// The to configure with check session endpoint support. + /// The configured , enabling further chaining of service registrations. + public static IServiceCollection AddCheckSessionEndpoint(this IServiceCollection services) + { + return services + .AddScoped(); + } + + /// + /// Adds services for handling user info requests, allowing clients to retrieve claims about the authenticated user + /// in accordance with OpenID Connect standards. + /// + /// + /// Registers scoped validators and processors for user info requests, enabling the secure delivery of claims + /// about the authenticated session user to the client. This functionality is crucial for OpenID Connect-compliant + /// applications, providing a standardized method for clients to access user profile information based on the scopes + /// and permissions granted during authentication. The service setup ensures that user info requests are properly + /// validated and processed, safeguarding sensitive user information while supporting rich client applications. + /// + /// The to configure with user info endpoint capabilities. + /// The configured , allowing for further service registration chaining. + public static IServiceCollection AddUserInfoEndpoint(this IServiceCollection services) + { + return services + .AddScoped() + .AddScoped() + .AddScoped(); + } + + /// + /// Registers services and validators for dynamic client registration and management endpoints, + /// based on the provided options factory. + /// + /// + /// This method enables the dynamic registration, reading, and removal of OAuth 2.0 clients at runtime, + /// facilitating flexible and scalable client management. + /// + /// The to configure. + /// A factory function to setup options for new client registrations. + /// The configured . + public static IServiceCollection AddDynamicClientEndpoints( + this IServiceCollection services, + Func newClientOptionsFactory) + { + return services + .AddClientRegistrationContextValidators() + + .AddSingleton() + .AddTransient(newClientOptionsFactory) + + .AddScoped() + .AddScoped() + .AddScoped() + + .AddScoped() + + .AddScoped() + .AddScoped() + + .AddScoped() + .AddScoped(); + } + + private static IServiceCollection AddClientRegistrationContextValidators(this IServiceCollection services) + { + return services + // compose ClientRegistrationContext validation as a pipeline of several IClientRegistrationContextValidator + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Compose(); + } + + /// + /// Adds services for handling end session (logout) requests aligning with OpenID Connect session management + /// specifications. This setup enables the application to handle logout requests effectively, ensuring that + /// user sessions are terminated securely across all involved parties. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddEndSessionEndpoint(this IServiceCollection services) + { + return services + .AddEndSessionContextValidators() + .AddScoped() + .AddScoped() + .AddScoped(); + } + + public static IServiceCollection AddEndSessionContextValidators(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Compose(); + } + + /// + /// Configures services for handling back-channel authentication requests, enabling secure server-to-server + /// authentication flows. + /// + /// The to configure. + /// The configured . + public static IServiceCollection AddBackChannelAuthenticationEndpoint(this IServiceCollection services) + { + return services + .AddScoped() + .AddScoped() + .AddScoped(); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/AuthorizationCodeReusePreventingDecorator.cs b/Abblix.Oidc.Server/Endpoints/Token/AuthorizationCodeReusePreventingDecorator.cs new file mode 100644 index 00000000..a33f6de7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/AuthorizationCodeReusePreventingDecorator.cs @@ -0,0 +1,131 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens.Revocation; + +namespace Abblix.Oidc.Server.Endpoints.Token; + +/// +/// Enhances token processing by revoking tokens associated with previously used authorization codes, +/// preventing authorization code reuse in compliance with OAuth 2.0 security best practices. +/// +/// +/// This class decorates the standard token request processing flow with additional security measures +/// to ensure the integrity of the authorization process. It detects when an authorization code, +/// which should only be used once, is attempted to be used multiple times. In such cases, it revokes any +/// tokens previously issued with that code and denies the request, effectively mitigating potential +/// security risks associated with code reuse. +/// +public class AuthorizationCodeReusePreventingDecorator: ITokenRequestProcessor +{ + /// + /// Initializes a new instance of the class, incorporating token revocation logic into the token request + /// processing pipeline. + /// + /// The underlying token request processor to be enhanced. + /// The registry used for managing token states and revocation. + /// + /// The service responsible for managing the lifecycle of authorization codes. + public AuthorizationCodeReusePreventingDecorator( + ITokenRequestProcessor processor, + ITokenRegistry tokenRegistry, + IAuthorizationCodeService authorizationCodeService) + { + _processor = processor; + _tokenRegistry = tokenRegistry; + _authorizationCodeService = authorizationCodeService; + } + + private readonly ITokenRequestProcessor _processor; + private readonly ITokenRegistry _tokenRegistry; + private readonly IAuthorizationCodeService _authorizationCodeService; + + /// + /// Processes a valid token request, including revoking existing tokens if necessary and registering new tokens. + /// + /// The valid token request to process. + /// + /// A task that represents the asynchronous operation, resulting in a . + /// + public async Task ProcessAsync(ValidTokenRequest request) + { + if (request is not { + Model: { GrantType: GrantTypes.AuthorizationCode, Code: {} code }, + AuthorizedGrant.IssuedTokens: var issuedTokens }) + { + return await _processor.ProcessAsync(request); + } + + // Handle revocation for used authorization codes and their associated tokens + if (issuedTokens is { Length: > 0 }) + { + // code was already used to issue some tokens, so we have to revoke all these tokens for security reason + await _authorizationCodeService.RemoveAuthorizationCodeAsync(code); + + foreach (var (jwtId, expiresAt) in issuedTokens) + { + await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Revoked, expiresAt); + } + + return new TokenErrorResponse( + ErrorCodes.InvalidGrant, + "The authorization code was already used"); + } + + // Proceed with processing the request using the decorated processor + var response = await _processor.ProcessAsync(request); + + // Register issued tokens as part of the authorization code grant + if (response is TokenIssuedResponse + { + AccessToken: var accessToken, + RefreshToken: var refreshToken + }) + { + var issuedTokensList = new List(); + void TryRegisterToken(JsonWebToken? token) + { + if (token is { Payload: { JwtId: { } jwtId, ExpiresAt: { } expiresAt }}) + { + issuedTokensList.Add(new TokenInfo(jwtId, expiresAt)); + } + } + + TryRegisterToken(accessToken.Token); + TryRegisterToken(refreshToken?.Token); + + if (issuedTokensList.Count > 0) + { + await _authorizationCodeService.UpdateAuthorizationGrantAsync( + code, + request.AuthorizedGrant with { IssuedTokens = issuedTokensList.ToArray() }, + request.ClientInfo.AuthorizationCodeExpiresIn); + } + } + + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Grants/AuthorizationCodeGrantHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Grants/AuthorizationCodeGrantHandler.cs new file mode 100644 index 00000000..b519ce9b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Grants/AuthorizationCodeGrantHandler.cs @@ -0,0 +1,109 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Text; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Endpoints.Token.Grants; + +/// +/// Handles the authorization code grant type for OAuth 2.0. +/// This class validates the provided authorization code against stored codes, +/// checks the client details, and implements PKCE verification when necessary. +/// +public class AuthorizationCodeGrantHandler : IAuthorizationGrantHandler +{ + /// + /// Initializes a new instance of the class. + /// + /// The service to validate request parameters. + /// The service to manage authorization codes. + public AuthorizationCodeGrantHandler( + IParameterValidator parameterValidator, + IAuthorizationCodeService authorizationCodeService) + { + _parameterValidator = parameterValidator; + _authorizationCodeService = authorizationCodeService; + } + + private readonly IParameterValidator _parameterValidator; + private readonly IAuthorizationCodeService _authorizationCodeService; + + /// + /// Gets the grant type this handler supports, which is the authorization code grant type. + /// + public IEnumerable GrantTypesSupported + { + get { yield return GrantTypes.AuthorizationCode; } + } + + /// + /// Authorizes the token request asynchronously using the authorization code grant type. + /// Validates the authorization code, verifies the client information, and ensures compliance with PKCE if used. + /// + /// The token request containing the authorization code. + /// The client information associated with the request. + /// A task representing the result of the authorization process, + /// containing a . + public async Task AuthorizeAsync(TokenRequest request, ClientInfo clientInfo) + { + _parameterValidator.Required(request.Code, nameof(request.Code)); + + var grantResult = await _authorizationCodeService.AuthorizeByCodeAsync(request.Code); + + return grantResult switch + { + AuthorizedGrant { Context.ClientId: var clientId } when clientId != clientInfo.ClientId + => new InvalidGrantResult(ErrorCodes.UnauthorizedClient, "Code was issued for another client"), + + AuthorizedGrant { Context.CodeChallenge: not null } when string.IsNullOrEmpty(request.CodeVerifier) + => new InvalidGrantResult(ErrorCodes.InvalidGrant, "Code verifier is required"), + + AuthorizedGrant { Context: { CodeChallenge: { } challenge, CodeChallengeMethod: { } method } } + when !string.Equals(challenge, CalculateChallenge(method, request.CodeVerifier), StringComparison.OrdinalIgnoreCase) + => new InvalidGrantResult(ErrorCodes.InvalidGrant, "Code verifier is not valid"), + + _ => grantResult, + }; + } + + /// + /// Calculates the code challenge based on the provided method and code verifier. + /// Supports 'plain' and 'S256' challenge methods. + /// + /// The code challenge method used during authorization request. + /// The code verifier submitted by the client. + /// The calculated code challenge string. + private static string CalculateChallenge(string method, string codeVerifier) => method switch + { + CodeChallengeMethods.S256 => HttpServerUtility.UrlTokenEncode(SHA256.HashData(Encoding.ASCII.GetBytes(codeVerifier))), + CodeChallengeMethods.Plain => codeVerifier, + _ => throw new ArgumentOutOfRangeException(nameof(method), $"Unknown code challenge method: {method}"), + }; +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Grants/CompositeAuthorizationGrantHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Grants/CompositeAuthorizationGrantHandler.cs new file mode 100644 index 00000000..3af6fe9d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Grants/CompositeAuthorizationGrantHandler.cs @@ -0,0 +1,81 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Token.Grants; + +/// +/// Represents a composite handler that manages multiple authorization grant handlers. This class allows for the delegation +/// of authorization requests to specific handlers based on the grant type specified in the request. It supports a dynamic +/// registration of grant handlers, facilitating the extension and customization of the authorization process to accommodate +/// various grant types defined by the OAuth 2.0 specification. +/// +public class CompositeAuthorizationGrantHandler: IAuthorizationGrantHandler +{ + public CompositeAuthorizationGrantHandler(IEnumerable grantHandlers) + { + _grantHandlers = new Dictionary( + from handler in grantHandlers + from grantType in handler.GrantTypesSupported + select new KeyValuePair(grantType, handler), + StringComparer.OrdinalIgnoreCase); + } + + private readonly Dictionary _grantHandlers; + + /// + /// The collection of grant types supported by the composite handler, aggregating the grant types + /// from all registered individual grant handlers. + /// + public IEnumerable GrantTypesSupported => _grantHandlers.Keys; + + /// + /// Asynchronously authorizes a token request based on its grant type. Delegates the authorization + /// process to the appropriate grant handler that supports the specified grant type in the request. + /// + /// The token request containing the grant type and other relevant data. + /// Client information associated with the request, used for validation and processing. + /// A task that resolves to a , + /// indicating the outcome of the authorization attempt. + public async Task AuthorizeAsync(TokenRequest request, ClientInfo clientInfo) + { + if (!_grantHandlers.TryGetValue(request.GrantType, out var grantHandler)) + { + return new InvalidGrantResult( + ErrorCodes.UnsupportedGrantType, + "The grant type is not supported"); + } + + if (!clientInfo.AllowedGrantTypes.Contains(request.GrantType)) + { + return new InvalidGrantResult( + ErrorCodes.UnauthorizedClient, + "The grant type is not allowed for this client"); + } + + return await grantHandler.AuthorizeAsync(request, clientInfo); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Grants/IAuthorizationGrantHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Grants/IAuthorizationGrantHandler.cs new file mode 100644 index 00000000..a04e0630 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Grants/IAuthorizationGrantHandler.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Token.Grants; + + +/// +/// Defines the interface for handling specific types of authorization grants within an OAuth 2.0 or OpenID Connect +/// context. Implementations of this interface are responsible for processing authorization requests based on +/// the supported grant type, validating the request, and generating appropriate authorization responses. +/// +public interface IAuthorizationGrantHandler +{ + /// + /// The grant types this handler is responsible for. + /// + IEnumerable GrantTypesSupported { get; } + + /// + /// Processes an authorization request asynchronously, validates the request against the supported grant type + /// and generates a response indicating the authorization result. + /// + /// The authorization request containing required parameters for the grant type. + /// Client information associated with the request, used for validation and generating + /// the response. + /// A task that when completed successfully, returns a GrantAuthorizationResult indicating the outcome + /// of the authorization process. + Task AuthorizeAsync(TokenRequest request, ClientInfo clientInfo); +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Grants/PasswordGrantHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Grants/PasswordGrantHandler.cs new file mode 100644 index 00000000..17909e6c --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Grants/PasswordGrantHandler.cs @@ -0,0 +1,79 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.Token.Grants; + +/// +/// This class is responsible for handling the password grant type +/// as part of the IAuthorizationGrantHandler process. +/// +public class PasswordGrantHandler : IAuthorizationGrantHandler +{ + /// + /// Initializes a new instance of the PasswordGrantHandler class. + /// + public PasswordGrantHandler( + IParameterValidator parameterValidator, + IUserCredentialsAuthenticator userCredentialsAuthenticator) + { + _parameterValidator = parameterValidator; + _userCredentialsAuthenticator = userCredentialsAuthenticator; + } + + private readonly IParameterValidator _parameterValidator; + private readonly IUserCredentialsAuthenticator _userCredentialsAuthenticator; + + /// + /// Gets the grant type this handler supports. + /// + public IEnumerable GrantTypesSupported + { + get { yield return GrantTypes.Password; } + } + + /// + /// Authorizes the token request asynchronously using the password grant type. + /// + /// The token request to authorize. + /// The client information associated with the request. + /// A task representing the result of the authorization process, containing a GrantAuthorizationResult object. + public Task AuthorizeAsync(TokenRequest request, ClientInfo clientInfo) + { + _parameterValidator.Required(request.UserName, nameof(request.UserName)); + _parameterValidator.Required(request.Password, nameof(request.Password)); + + var userName = request.UserName; + var password = request.Password; + var scope = request.Scope; + var context = new AuthorizationContext(clientInfo.ClientId, scope, null); + + return _userCredentialsAuthenticator.ValidateAsync(userName, password, context); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Grants/RefreshTokenGrantHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Grants/RefreshTokenGrantHandler.cs new file mode 100644 index 00000000..281f16d6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Grants/RefreshTokenGrantHandler.cs @@ -0,0 +1,105 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Features.Tokens.Revocation; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Token.Grants; + +/// +/// This class is responsible for handling the refresh token grant type +/// as part of the IAuthorizationGrantHandler process. +/// +public class RefreshTokenGrantHandler : IAuthorizationGrantHandler +{ + /// + /// Initializes a new instance of the RefreshTokenGrantHandler class. + /// + public RefreshTokenGrantHandler( + IParameterValidator parameterValidator, + IAuthServiceJwtValidator jwtValidator, + IRefreshTokenService refreshTokenService, + ITokenRegistry tokenRegistry) + { + _parameterValidator = parameterValidator; + _jwtValidator = jwtValidator; + _refreshTokenService = refreshTokenService; + _tokenRegistry = tokenRegistry; + } + + private readonly IParameterValidator _parameterValidator; + private readonly IAuthServiceJwtValidator _jwtValidator; + private readonly IRefreshTokenService _refreshTokenService; + private readonly ITokenRegistry _tokenRegistry; + + /// + /// Gets the grant type this handler supports. + /// + public IEnumerable GrantTypesSupported + { + get { yield return GrantTypes.RefreshToken; } + } + + /// + /// Authorizes the token request asynchronously using the refresh token grant type. + /// + /// The token request to authorize. + /// The client information associated with the request. + /// A task representing the result of the authorization process, + /// containing a GrantAuthorizationResult object. + public async Task AuthorizeAsync(TokenRequest request, ClientInfo clientInfo) + { + _parameterValidator.Required(request.RefreshToken, nameof(request.RefreshToken)); + + var jwtValidationResult = await _jwtValidator.ValidateAsync(request.RefreshToken); + + switch (jwtValidationResult) + { + case ValidJsonWebToken { Token: var token, Token.Header.Type: var tokenType }: + if (tokenType != JwtTypes.RefreshToken) + return new InvalidGrantResult(ErrorCodes.InvalidGrant, $"Invalid token type: {tokenType}"); + + var result = await _refreshTokenService.AuthorizeByRefreshTokenAsync(token); + if (result is AuthorizedGrant { Context.ClientId: var clientId } && clientId != clientInfo.ClientId) + return new InvalidGrantResult(ErrorCodes.InvalidGrant, "The specified grant belongs to another client"); + + return result; + + case JwtValidationError error: + return new InvalidGrantResult(ErrorCodes.InvalidGrant, error.ErrorDescription); + + default: + throw new UnexpectedTypeException(nameof(jwtValidationResult), jwtValidationResult.GetType()); + } + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/AuthorizedGrant.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/AuthorizedGrant.cs new file mode 100644 index 00000000..a403a04a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/AuthorizedGrant.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.UserAuthentication; + + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents the successful result of an authorized grant operation, +/// including authentication session and authorization context. +/// +/// The authentication session associated with the grant. +/// The context of the authorization process. +public record AuthorizedGrant(AuthSession AuthSession, AuthorizationContext Context) + : GrantAuthorizationResult +{ + public TokenInfo[]? IssuedTokens { get; init; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/GrantAuthorizationResult.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/GrantAuthorizationResult.cs new file mode 100644 index 00000000..5b23a463 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/GrantAuthorizationResult.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents the base result of a grant authorization process. +/// +public abstract record GrantAuthorizationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenHandler.cs new file mode 100644 index 00000000..63dccc17 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenHandler.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Defines a contract for handling OAuth 2.0 token requests, encompassing validation, processing, and issuance +/// of tokens based on authorization grants. +/// +public interface ITokenHandler +{ + /// + /// Asynchronously handles a token request, validating the request details and, if valid, processing it to issue, + /// renew or exchange tokens according to OAuth 2.0 and OpenID Connect standards. + /// + /// The token request containing essential parameters such as the grant type, + /// client credentials, and other parameters pertinent to the token issuance process. + /// Supplementary information about the client making the request, necessary + /// for performing contextual validation and ensuring the request complies with security policies. + /// + /// A resulting in a , which either contains the issued tokens + /// (access token, refresh token, ID token, etc.) in case of a successful request or details the reasons + /// for request failure. + /// + /// + /// Implementations of this interface are critical to the secure and compliant functioning of an OAuth 2.0 + /// authorization server. They must ensure that only valid and authorized requests lead to the issuance of tokens, + /// thereby maintaining the integrity and security of the authentication and authorization process. + /// + Task HandleAsync(TokenRequest tokenRequest, ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestProcessor.cs new file mode 100644 index 00000000..a23bedf1 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestProcessor.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Processes incoming token requests from clients, ensuring they are valid and authorized before issuing +/// the appropriate token response. Depending on the request type and granted permissions, the response can include +/// various types of tokens such as Access Tokens, Refresh Tokens and ID Tokens. +/// +/// +/// This interface abstracts the core logic behind token issuance in compliance with OAuth 2.0 and OpenID Connect +/// standards. Implementations are responsible for validating the token request details, determining the types of tokens +/// to issue based on the request's scope and authorization, and generating a token response that conforms to +/// the protocol specifications. While the typical response includes an Access Token and, in the case of OpenID Connect, +/// an ID Token, the exact contents of the response may vary based on the request parameters and server policies. +/// +public interface ITokenRequestProcessor +{ + /// + /// Asynchronously processes a validated and authorized token request, generating a token response. + /// + /// The validated token request from the client. + /// A task that resolves to a , encapsulating the tokens to be issued to + /// the client. + Task ProcessAsync(ValidTokenRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestValidator.cs new file mode 100644 index 00000000..061ceed2 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ITokenRequestValidator.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// The Authorization Server MUST validate the Token Request as follows: +/// +/// - Authenticate the Client if it was issued Client Credentials or if it uses another Client Authentication method. +/// - Ensure the Authorization Code was issued to the authenticated Client. +/// - Verify that the Authorization Code is valid. +/// - If possible, verify that the Authorization Code has not been previously used. +/// - Ensure that the redirect_uri parameter value is identical to the redirect_uri parameter value +/// that was included in the initial Authorization Request. +/// If the redirect_uri parameter value is not present when there is only one registered redirect_uri value, +/// the Authorization Server MAY return an error (since the Client should have included the parameter) +/// or MAY proceed without an error (since OAuth 2.0 permits the parameter to be omitted in this case). +/// - Verify that the Authorization Code used was issued in response to an OpenID Connect Authentication Request +/// (so that an ID Token will be returned from the Token Endpoint). +/// +public interface ITokenRequestValidator +{ + Task ValidateAsync(TokenRequest tokenRequest, ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/InvalidGrantResult.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/InvalidGrantResult.cs new file mode 100644 index 00000000..19d3652d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/InvalidGrantResult.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents an invalid grant result with error details. +/// +/// The error code indicating the nature of the failure. +/// A human-readable description of the error. +public record InvalidGrantResult(string Error, string ErrorDescription) : GrantAuthorizationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/RefreshTokenAuthorizedGrant.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/RefreshTokenAuthorizedGrant.cs new file mode 100644 index 00000000..d50e5e45 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/RefreshTokenAuthorizedGrant.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents an authorized grant result for a refresh token request. +/// Contains the authenticated session, authorization context, and the associated refresh token. +/// +/// The authenticated user session, which includes information about the user's +/// authentication state. +/// The authorization context containing details about the current authorization process, +/// such as requested scopes and client information. +/// The refresh token associated with the authorized grant, +/// used to obtain new access tokens without requiring further user interaction. +public record RefreshTokenAuthorizedGrant( + AuthSession AuthSession, + AuthorizationContext Context, + JsonWebToken RefreshToken) + : AuthorizedGrant(AuthSession, Context); diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenErrorResponse.cs new file mode 100644 index 00000000..ceffd2c4 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenErrorResponse.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents an error response in the token endpoint of an OAuth flow. +/// +/// The error code indicating why the token request failed. +/// A detailed description of the error. +public record TokenErrorResponse(string Error, string ErrorDescription) : TokenResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenInfo.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenInfo.cs new file mode 100644 index 00000000..fa197a47 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenInfo.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Contains information about an issued token. +/// +/// The unique identifier of the JWT. +/// The expiration time of the token, if applicable. +public record TokenInfo(string JwtId, DateTimeOffset ExpiresAt); diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenIssuedResponse.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenIssuedResponse.cs new file mode 100644 index 00000000..4ad1c25b --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenIssuedResponse.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.Tokens; + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents a successful response from the token endpoint, containing the issued access token and related information. +/// +/// The access token issued by the authorization server. +/// The type of the token issued. +/// The lifetime in seconds of the access token. +/// The URI identifying the type of the issued token. +public record TokenIssuedResponse(EncodedJsonWebToken AccessToken, string TokenType, TimeSpan ExpiresIn, Uri IssuedTokenType) + : TokenResponse +{ + /// + /// The optional refresh token that can be used to obtain new access tokens. + /// + public EncodedJsonWebToken? RefreshToken { get; set; } + + /// + /// An ID token that provides identity information about the user. + /// + public EncodedJsonWebToken? IdToken { get; set; } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestError.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestError.cs new file mode 100644 index 00000000..ddc9c1c9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestError.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents an error in the token request validation process. +/// +/// The error code indicating the nature of the failure. +/// A human-readable description of the error. +public record TokenRequestError(string Error, string ErrorDescription) : TokenRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestValidationResult.cs new file mode 100644 index 00000000..b304c9ab --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenRequestValidationResult.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents the base result of a token request validation process. +/// +public abstract record TokenRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenResponse.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenResponse.cs new file mode 100644 index 00000000..3fe61826 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/TokenResponse.cs @@ -0,0 +1,28 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents the base response from a token endpoint. +/// +public abstract record TokenResponse; diff --git a/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ValidTokenRequest.cs b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ValidTokenRequest.cs new file mode 100644 index 00000000..3ea6a8bb --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/Interfaces/ValidTokenRequest.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +/// +/// Represents a valid token request along with related authentication and authorization information. +/// +/// The token request model containing the information required to process the token request. +/// +/// The authorized grant result which encapsulates the result of the authorization +/// process. +/// Information about the client making the token request, including client credentials and +/// metadata. +public record ValidTokenRequest( + TokenRequest Model, + AuthorizedGrant AuthorizedGrant, + ClientInfo ClientInfo) : TokenRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/Token/TokenHandler.cs b/Abblix.Oidc.Server/Endpoints/Token/TokenHandler.cs new file mode 100644 index 00000000..ed08dbad --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/TokenHandler.cs @@ -0,0 +1,90 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Model; +using TokenResponse = Abblix.Oidc.Server.Endpoints.Token.Interfaces.TokenResponse; + +namespace Abblix.Oidc.Server.Endpoints.Token; + +/// +/// Manages the processing of token requests according to OAuth 2.0 and OpenID Connect specifications. +/// This includes validating the request for compliance with the protocol requirements and processing it to issue, +/// renew or exchange tokens as appropriate. +/// +public class TokenHandler : ITokenHandler +{ + /// + /// Constructs a new instance of the with specified validator and processor + /// for handling token requests. + /// + /// An implementation of responsible for ensuring + /// that token requests meet the required validation criteria. + /// An implementation of responsible for executing + /// the logic necessary to issue, renew, or exchange tokens based on validated requests. + public TokenHandler(ITokenRequestValidator validator, ITokenRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly ITokenRequestValidator _validator; + private readonly ITokenRequestProcessor _processor; + + /// + /// Asynchronously handles a token request by first validating it and then, if the validation is successful, + /// processing the request to issue, renew, or exchange tokens as required by the request parameters. + /// + /// An object containing the details of the token request, including the grant type, + /// client credentials and other necessary parameters. + /// Additional information about the client making the request, used for contextual + /// validation. + /// + /// A that resolves to a , indicating the outcome of + /// the request handling. This response can include the issued tokens in case of success, or an error response + /// detailing the reason for failure if the request does not pass validation or cannot be processed. + /// + /// + /// This method is integral to the security and functionality of the OAuth 2.0 and OpenID Connect framework, + /// ensuring that only valid and authorized requests result in the issuance, renewal, or exchange of tokens. + /// It employs rigorous validation to prevent unauthorized access and to maintain the integrity of the token + /// lifecycle management process. + /// + public async Task HandleAsync( + TokenRequest tokenRequest, + ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(tokenRequest, clientRequest); + + var response = validationResult switch + { + ValidTokenRequest validRequest => await _processor.ProcessAsync(validRequest), + + TokenRequestError { Error: var error, ErrorDescription: var description } + => new TokenErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()) + }; + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/TokenRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/Token/TokenRequestProcessor.cs new file mode 100644 index 00000000..cc929639 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/TokenRequestProcessor.cs @@ -0,0 +1,118 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.Tokens; + + +namespace Abblix.Oidc.Server.Endpoints.Token; + +/// +/// Processes token requests in compliance with OAuth 2.0 and OpenID Connect standards. +/// This processor is responsible for handling various types of token requests (e.g., authorization code, refresh token) +/// and generating the appropriate token responses, including access tokens, refresh tokens and ID tokens. +/// +public class TokenRequestProcessor : ITokenRequestProcessor +{ + /// + /// Initializes a new instance of the class with services for token generation + /// and management. + /// + /// Service for creating and managing access tokens. + /// Service for creating and managing refresh tokens. + /// Service for creating and managing ID tokens in OpenID Connect flows. + public TokenRequestProcessor( + IAccessTokenService accessTokenService, + IRefreshTokenService refreshTokenService, + IIdentityTokenService identityTokenService) + { + _accessTokenService = accessTokenService; + _refreshTokenService = refreshTokenService; + _identityTokenService = identityTokenService; + } + + private readonly IAccessTokenService _accessTokenService; + private readonly IRefreshTokenService _refreshTokenService; + private readonly IIdentityTokenService _identityTokenService; + + /// + /// Asynchronously processes a valid token request, determining the necessary tokens to generate based on + /// the request's scope and grant type. Generates an access token for every request and, depending on the scope, + /// may also generate a refresh token and an ID token for OpenID Connect authentication. + /// + /// + /// The validated token request containing client and authorization session information. + /// A task representing the asynchronous operation, yielding a containing + /// the generated tokens. + /// + /// Access tokens are generated for client authorization in resource access. + /// Refresh tokens are issued for long-lived sessions, allowing clients to obtain new access tokens without + /// re-authentication. ID tokens provide identity information about the user and are used in OpenID Connect + /// authentication flows. This method ensures the secure and compliant generation of these tokens as per OAuth 2.0 + /// and OpenID Connect standards. + /// + public async Task ProcessAsync(ValidTokenRequest request) + { + request.ClientInfo.CheckClient(); + + var accessToken = await _accessTokenService.CreateAccessTokenAsync( + request.AuthorizedGrant.AuthSession, + request.AuthorizedGrant.Context, + request.ClientInfo); + + var response = new TokenIssuedResponse( + accessToken, + TokenTypes.Bearer, + request.ClientInfo.AccessTokenExpiresIn, + TokenTypeIdentifiers.AccessToken); + + if (request.AuthorizedGrant.Context.Scope.HasFlag(Scopes.OfflineAccess)) + { + response.RefreshToken = await _refreshTokenService.CreateRefreshTokenAsync( + request.AuthorizedGrant.AuthSession, + request.AuthorizedGrant.Context, + request.ClientInfo, + request.AuthorizedGrant switch + { + RefreshTokenAuthorizedGrant grant => grant.RefreshToken, + _ => null, + }); + } + + if (request.AuthorizedGrant.Context.Scope.HasFlag(Scopes.OpenId)) + { + response.IdToken = await _identityTokenService.CreateIdentityTokenAsync( + request.AuthorizedGrant.AuthSession, + request.AuthorizedGrant.Context, + request.ClientInfo, + false, + null, + accessToken.EncodedJwt); + } + + return response; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/Token/TokenRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Token/TokenRequestValidator.cs new file mode 100644 index 00000000..06612fcf --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/Token/TokenRequestValidator.cs @@ -0,0 +1,114 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.Token.Grants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; + + +namespace Abblix.Oidc.Server.Endpoints.Token; + +/// +/// Validates token requests against OAuth 2.0 specifications, ensuring that requests are properly formed and authorized. +/// This class plays a critical role in the OAuth 2.0 authentication and authorization process by verifying the integrity and +/// authenticity of token requests according to the framework defined in RFC 6749. +/// +public class TokenRequestValidator : ITokenRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// Sets up the validator with the necessary components for client authentication and handling different + /// authorization grant types. Client authentication follows the process described in RFC 6749, Section 2.3. + /// + /// The authenticator used for validating client requests. + /// A grant handler responsible for different types of authorization grants. + public TokenRequestValidator( + IClientAuthenticator clientAuthenticator, + IAuthorizationGrantHandler grantHandler) + { + _clientAuthenticator = clientAuthenticator; + _grantHandler = grantHandler; + } + + private readonly IClientAuthenticator _clientAuthenticator; + private readonly IAuthorizationGrantHandler _grantHandler; + + /// + /// Asynchronously validates a token request against the OAuth 2.0 specifications. It checks for proper authorization + /// of the client, the validity of the grant type, and other request parameters. This process involves authenticating + /// the client using the provided client authenticator and then delegating the grant-specific validation + /// to the appropriate grant handler. + /// + /// The token request containing all necessary parameters for validation. + /// Client request information necessary for client authentication. + /// A that resolves to a , + /// indicating the outcome of the validation process. This result can either denote a successful validation + /// or contain error information specifying why the request was invalid. + public async Task ValidateAsync(TokenRequest tokenRequest, ClientRequest clientRequest) + { + if (tokenRequest.Resource != null) + { + foreach (var resource in tokenRequest.Resource) + { + if (!resource.IsAbsoluteUri) + { + return new TokenRequestError( + ErrorCodes.InvalidTarget, + "The resource must be absolute URI"); + } + if (resource.Fragment.HasValue()) + { + return new TokenRequestError( + ErrorCodes.InvalidTarget, + "The resource must not contain fragment"); + } + } + } + + var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest); + if (clientInfo == null) + { + return new TokenRequestError(ErrorCodes.InvalidClient, "The client is not authorized"); + } + + var result = await _grantHandler.AuthorizeAsync(tokenRequest, clientInfo); + return result switch + { + InvalidGrantResult { Error: var error, ErrorDescription: var description } + => new TokenRequestError(error, description), + + AuthorizedGrant { Context.RedirectUri: var redirectUri } when redirectUri != tokenRequest.RedirectUri + => new TokenRequestError( + ErrorCodes.InvalidGrant, + "The redirect Uri value does not match to the value used before"), + + AuthorizedGrant grant + => new ValidTokenRequest(tokenRequest, grant, clientInfo), + + _ => throw new UnexpectedTypeException(nameof(result), result.GetType()), + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoHandler.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoHandler.cs new file mode 100644 index 00000000..83c43eb2 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoHandler.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Defines a contract for handling UserInfo requests within an OAuth 2.0 and OpenID Connect framework. +/// It outlines the necessary operations for processing such requests, ensuring they adhere to security +/// and protocol standards. +/// +public interface IUserInfoHandler +{ + /// + /// Asynchronously handles a UserInfo request, validating the request for authorization and processing it to + /// return the requested user information. + /// + /// The user info request containing the access token and possibly other parameters + /// defining the scope of information requested. + /// Additional client-specific request information that may be necessary for processing + /// the request in certain contexts. + /// + /// A that resolves to a , which contains the user information + /// if the request is authorized and valid, or an error response indicating why the request could not be fulfilled. + /// + /// + /// This method plays a crucial role in the OAuth 2.0 and OIDC ecosystems by enabling secure access to user + /// information based on authorized requests. Implementations should ensure that the access token provided + /// in the UserInfo request is validated and that any returned information is consistent with the scopes + /// granted during the authorization process. + /// + Task HandleAsync( + UserInfoRequest userInfoRequest, + ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestProcessor.cs new file mode 100644 index 00000000..e85182c9 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestProcessor.cs @@ -0,0 +1,37 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Generates a response containing information about a user account. +/// +public interface IUserInfoRequestProcessor +{ + /// + /// Asynchronously processes a valid user info request and generates a user info response. + /// + /// The valid user info request to process. + /// A representing the asynchronous operation, + /// which upon completion will yield a . + Task ProcessAsync(ValidUserInfoRequest request); +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestValidator.cs new file mode 100644 index 00000000..330b4175 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoRequestValidator.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Model; + + + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Parses and validates an access token provided in a user info request. +/// +public interface IUserInfoRequestValidator +{ + /// + /// Asynchronously validates a user info request and generates a validation result. + /// + /// The user info request to validate. + /// Additional client request information for contextual validation. + /// A representing the asynchronous operation, + /// which upon completion will yield a . + Task ValidateAsync(UserInfoRequest userInfoRequest, ClientRequest clientRequest); +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoErrorResponse.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoErrorResponse.cs new file mode 100644 index 00000000..e921ec06 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoErrorResponse.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents an error response in the user information request process. +/// +/// The error code indicating the nature of the error. +/// A human-readable description of the error. +public record UserInfoErrorResponse(string Error, string ErrorDescription) : UserInfoResponse; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoFoundResponse.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoFoundResponse.cs new file mode 100644 index 00000000..545a769a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoFoundResponse.cs @@ -0,0 +1,35 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Oidc.Server.Features.ClientInformation; + + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents a successful response containing the found user information. +/// +/// The collection of JWT claims associated with the user. +/// Information about the client making the request. +public record UserInfoFoundResponse(JsonObject User, ClientInfo ClientInfo, string Issuer) + : UserInfoResponse; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestError.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestError.cs new file mode 100644 index 00000000..a3321c4a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestError.cs @@ -0,0 +1,30 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents an error encountered during the validation of a user info request. +/// +/// The error code indicating the nature of the validation error. +/// A human-readable description of the validation error. +public record UserInfoRequestError(string Error, string ErrorDescription) : UserInfoRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestValidationResult.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestValidationResult.cs new file mode 100644 index 00000000..1002ff17 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoRequestValidationResult.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents the base class for the result of a user info request validation. +/// This class is intended to be extended by specific validation result types. +/// +public record UserInfoRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoResponse.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoResponse.cs new file mode 100644 index 00000000..c272d34a --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/UserInfoResponse.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents the base class for a response to a user info request. +/// This class is intended to be extended by specific response types. +/// +public record UserInfoResponse; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/ValidUserInfoRequest.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/ValidUserInfoRequest.cs new file mode 100644 index 00000000..099957b6 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/ValidUserInfoRequest.cs @@ -0,0 +1,42 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; + + +namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; + +/// +/// Represents a valid user info request with associated authentication and authorization details. +/// +/// The user info request model. +/// The authentication session associated with the request. +/// The authorization context of the request. +/// The client information for the request. +public record ValidUserInfoRequest( + UserInfoRequest Model, + AuthSession AuthSession, + AuthorizationContext AuthContext, + ClientInfo ClientInfo) : UserInfoRequestValidationResult; diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoHandler.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoHandler.cs new file mode 100644 index 00000000..25542aad --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoHandler.cs @@ -0,0 +1,87 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Endpoints.UserInfo; + +/// +/// Handles user information requests in an OpenID Connect compliant manner. It ensures that requests for user info +/// are correctly validated and processed, returning the requested user information if the request is authorized. +/// +public class UserInfoHandler : IUserInfoHandler +{ + /// + /// Initializes a new instance of the class, setting up the necessary validator + /// and processor for handling user info requests. + /// + /// An implementation of responsible for validating + /// user info requests against OpenID Connect specifications. + /// An implementation of responsible for processing + /// validated requests and retrieving user information. + public UserInfoHandler( + IUserInfoRequestValidator validator, + IUserInfoRequestProcessor processor) + { + _validator = validator; + _processor = processor; + } + + private readonly IUserInfoRequestValidator _validator; + private readonly IUserInfoRequestProcessor _processor; + + /// + /// Asynchronously processes a user info request by first validating it and then, if validation is successful, + /// retrieving the requested user information. + /// + /// The user info request containing necessary parameters such as the access token. + /// + /// Additional information about the client making the request, useful for contextual + /// validation. + /// + /// A that resolves to a , which contains the requested user + /// information in case of a valid request, or an error detailing the reason for failure. + /// + /// + /// This method is pivotal for ensuring that only authenticated and authorized requests gain access to sensitive + /// user information, in line with OpenID Connect protocols. It leverages the validator to ensure requests meet + /// OIDC standards and the processor to fetch and return the relevant user information securely. + /// + public async Task HandleAsync( + UserInfoRequest userInfoRequest, + ClientRequest clientRequest) + { + var validationResult = await _validator.ValidateAsync(userInfoRequest, clientRequest); + + return validationResult switch + { + ValidUserInfoRequest validRequest => await _processor.ProcessAsync(validRequest), + + UserInfoRequestError { Error: var error, ErrorDescription: var description } + => new UserInfoErrorResponse(error, description), + + _ => throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()), + }; + } +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs new file mode 100644 index 00000000..dd834e2d --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs @@ -0,0 +1,81 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.UserInfo; +using UserInfoResponse = Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces.UserInfoResponse; + + +namespace Abblix.Oidc.Server.Endpoints.UserInfo; + +/// +/// Processes user information requests by retrieving and formatting user information based on the provided request. +/// This class is integral in handling requests to the UserInfo endpoint, ensuring that the returned user information +/// adheres to requested scopes and complies with OAuth 2.0 and OpenID Connect standards. +/// +internal class UserInfoRequestProcessor : IUserInfoRequestProcessor +{ + /// + /// Initializes a new instance of the class. + /// + /// Provider for the issuer URL, which is essential for generating fully qualified + /// claim names and ensuring consistency in the 'iss' claim across responses. + /// Provider for user claims based on JWT claims. + /// This component fetches user-related data that can be returned to the client, tailored to the client's + /// authorization context and scope. + public UserInfoRequestProcessor(IIssuerProvider issuerProvider, IUserClaimsProvider userClaimsProvider) + { + _issuerProvider = issuerProvider; + _userClaimsProvider = userClaimsProvider; + } + + private readonly IIssuerProvider _issuerProvider; + private readonly IUserClaimsProvider _userClaimsProvider; + + /// + /// Asynchronously processes a valid user information request and returns a structured response containing + /// the requested user information. + /// + /// The valid user information request containing the authentication session, + /// authorization context and client information necessary to determine the scope and specifics of + /// the requested claims. + /// A representing the asynchronous operation, + /// which upon completion will yield a encapsulating either the user's claims + /// or an error response. + public async Task ProcessAsync(ValidUserInfoRequest request) + { + var userInfo = await _userClaimsProvider.GetUserClaimsAsync( + request.AuthSession, + request.AuthContext.Scope, + request.AuthContext.RequestedClaims?.UserInfo, + request.ClientInfo); + + if (userInfo == null) + return new UserInfoErrorResponse(ErrorCodes.InvalidGrant, "The user claims aren't found"); + + var issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()); + return new UserInfoFoundResponse(userInfo, request.ClientInfo, issuer); + } +} diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs new file mode 100644 index 00000000..37065ca7 --- /dev/null +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs @@ -0,0 +1,141 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; +using static Abblix.Oidc.Server.Model.UserInfoRequest; + + + +namespace Abblix.Oidc.Server.Endpoints.UserInfo; + +/// +/// Validates user information requests by verifying the access token and ensuring the request conforms to expected standards. +/// Implements the interface. +/// +public class UserInfoRequestValidator : IUserInfoRequestValidator +{ + /// + /// Initializes a new instance of the class. + /// + /// The JWT validator used for validating the access tokens. + /// The service responsible for managing access tokens. + /// The provider for retrieving client information. + public UserInfoRequestValidator( + IAuthServiceJwtValidator jwtValidator, + IAccessTokenService accessTokenService, + IClientInfoProvider clientInfoProvider) + { + _jwtValidator = jwtValidator; + _accessTokenService = accessTokenService; + _clientInfoProvider = clientInfoProvider; + } + + private readonly IAuthServiceJwtValidator _jwtValidator; + private readonly IAccessTokenService _accessTokenService; + private readonly IClientInfoProvider _clientInfoProvider; + + /// + /// Asynchronously validates a user information request and determines its validity based on + /// the provided access token and request parameters. + /// + /// The user info request to validate. + /// Additional client request information for contextual validation. + /// A representing the asynchronous operation, + /// which upon completion will yield a . + public async Task ValidateAsync(UserInfoRequest userInfoRequest, ClientRequest clientRequest) + { + string jwtAccessToken; + var authorizationHeader = clientRequest.AuthorizationHeader; + if (authorizationHeader != null) + { + if (authorizationHeader.Scheme != TokenTypes.Bearer) + { + return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"The scheme name '{authorizationHeader.Scheme}' is not supported"); + } + + if (userInfoRequest.AccessToken != null) + { + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"The access token must be passed via '{HttpRequestHeaders.Authorization}' header " + + $"or '{Parameters.AccessToken}' parameter, but not in both sources at the same time"); + } + + if (authorizationHeader.Parameter == null) + { + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"The access token must be specified via '{HttpRequestHeaders.Authorization}' header"); + } + + jwtAccessToken = authorizationHeader.Parameter; + } + else if (userInfoRequest.AccessToken == null) + { + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"The access token must be passed via '{HttpRequestHeaders.Authorization}' header " + + $"or '{Parameters.AccessToken}' parameter, but none of them specified"); + } + else + { + jwtAccessToken = userInfoRequest.AccessToken; + } + + var result = await _jwtValidator.ValidateAsync(jwtAccessToken, ValidationOptions.Default & ~ValidationOptions.ValidateAudience); + + AuthSession? authSession; + AuthorizationContext? authContext; + + switch (result) + { + case ValidJsonWebToken { Token: { Header.Type: var tokenType } token }: + if (tokenType != JwtTypes.AccessToken) + return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"Invalid token type: {tokenType}"); + + (authSession, authContext) = await _accessTokenService.AuthenticateByAccessTokenAsync(token); + break; + + case JwtValidationError error: + return new UserInfoRequestError(ErrorCodes.InvalidGrant, error.ErrorDescription); + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + + var clientInfo = await _clientInfoProvider.TryFindClientAsync(authContext.ClientId).WithLicenseCheck(); + if (clientInfo == null) + return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"The client '{authContext.ClientId}' is not found"); + + return new ValidUserInfoRequest(userInfoRequest, authSession, authContext, clientInfo); + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretAuthenticator.cs new file mode 100644 index 00000000..c3e22187 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretAuthenticator.cs @@ -0,0 +1,178 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Serves as a base class for client authentication, utilizing client ID and secret. It validates +/// clients against a known list of clients, ensuring that the client secret provided during the authentication +/// process matches the stored secret for the client. This class supports various hash algorithms for +/// secure secret comparison and handles client secret expiration. +/// +public abstract class ClientSecretAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger for logging information and errors. + /// The provider used to retrieve client information. + /// The clock instance used for time-related operations, such as checking secret expiration. + /// The hasher used for hashing client secrets for secure comparison. + protected ClientSecretAuthenticator( + ILogger logger, + IClientInfoProvider clientInfoProvider, + TimeProvider clock, + IHashService hashService) + { + _logger = logger; + _clientInfoProvider = clientInfoProvider; + _clock = clock; + _hashService = hashService; + } + + private readonly ILogger _logger; + private readonly IClientInfoProvider _clientInfoProvider; + private readonly TimeProvider _clock; + private readonly IHashService _hashService; + + /// + /// Asynchronously authenticates a client using provided credentials. It validates the client ID and secret + /// against stored values, considering the authentication method and secret expiration. + /// + /// Client ID for identification. + /// Client secret for verification. + /// Authentication method used, ensuring compatibility with client configuration. + /// + /// Authenticated client information if successful; otherwise, null. + /// + protected async Task TryAuthenticateAsync(string? clientId, string? secret, string authenticationMethod) + { + if (clientId == null) + { + return null; + } + + var client = await _clientInfoProvider.TryFindClientAsync(clientId).WithLicenseCheck(); + if (client == null) + { + _logger.LogDebug("Client authentication failed: client information for id {ClientId} is missing", clientId); + return null; + } + + if (client.ClientSecrets?.Length == 0 || !secret.HasValue()) + { + _logger.LogDebug("Client authentication failed: no secrets are configured for client {ClientId}", clientId); + return null; + } + + if (client.TokenEndpointAuthMethod != authenticationMethod) { + _logger.LogDebug("Client authentication failed: client {ClientId} uses another authentication method", clientId); + return null; + } + + if (!TryValidateClientSecret(client, secret)) + { + return null; + } + + return client; + } + + /// + /// Validates a provided client secret against stored secrets for a given client. This method supports + /// secure comparison by hashing the provided secret and comparing it with stored hashes. + /// + /// The client whose secret is to be validated. + /// The secret provided for validation. + /// + /// True if the secret matches a stored hash and is not expired; otherwise, false. + /// + private bool TryValidateClientSecret(ClientInfo client, string secret) + { + // We store only client secret hashes, so we have to hash the raw secret to compare. And we do it lazy. + var matchingSha256Secrets = FindMatchingSecrets(client, + clientSecret => clientSecret.Sha512Hash, HashAlgorithm.Sha512, secret); + + var matchingSha512Secrets = FindMatchingSecrets(client, + clientSecret => clientSecret.Sha256Hash, HashAlgorithm.Sha256, secret); + + var matchingSecret = matchingSha256Secrets.Concat(matchingSha512Secrets) + .MaxBy(item => item.ExpiresAt); + + if (matchingSecret == null) + { + _logger.LogWarning("Client authentication failed: No matching secret found for client {ClientId}", + client.ClientId); + return false; // Invalid secret + } + + if (matchingSecret.ExpiresAt.HasValue && matchingSecret.ExpiresAt.Value < _clock.GetUtcNow()) + { + _logger.LogWarning("Client authentication failed: Secret has expired for client {ClientId}", + client.ClientId); + return false; // Secret is expired + } + + _logger.LogInformation("Client authenticated successfully with client ID {ClientId}", + client.ClientId); + return true; + } + + /// + /// Identifies stored secrets that match a provided secret value for a client. It hashes the provided + /// secret using the specified algorithm and compares it against stored hashes. + /// + /// Client owning the secrets. + /// Function selecting the hash from a client secret. + /// Algorithm used for hashing the provided secret. + /// Secret value to hash and compare. + /// + /// Enumerable of matching client secrets. + /// + private IEnumerable FindMatchingSecrets( + ClientInfo client, + Func hashSelector, + HashAlgorithm hashAlgorithm, + string secretValue) + { + var validSecretHashes = + from clientSecret in client.ClientSecrets + let secretHash = hashSelector(clientSecret) + where secretHash != null + select (clientSecret, secretHash); + + byte[]? hash = null; + foreach (var (clientSecret, validSecretHash) in validSecretHashes) + { + hash ??= _hashService.Sha(hashAlgorithm, secretValue); + + if (validSecretHash.SequenceEqual(hash)) + yield return clientSecret; + } + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretBasicAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretBasicAuthenticator.cs new file mode 100644 index 00000000..aa03add3 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretBasicAuthenticator.cs @@ -0,0 +1,101 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Implements an authentication of a client request by HTTP 'Authorization' header using the 'Basic' scheme. +/// This authentication method follows the standards outlined in RFC 7617. +/// +public class ClientSecretBasicAuthenticator : ClientSecretAuthenticator, IClientAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// Logger for logging authentication-related information and errors. + /// The provider for client information retrieval. + /// The clock instance for time-related operations. + /// The client secret hasher for verifying client secrets. + public ClientSecretBasicAuthenticator( + ILogger logger, + IClientInfoProvider clientInfoProvider, + TimeProvider clock, + IHashService hashService) + : base(logger, clientInfoProvider, clock, hashService) + { + } + + /// + /// Specifies the client authentication method this authenticator supports, which is 'client_secret_basic'. + /// This indicates that the authenticator handles client authentication using the Basic Authentication scheme, + /// as defined in RFC 7617, where the client ID and secret are passed in the 'Authorization' header + /// encoded in Base64 format. + /// + public IEnumerable ClientAuthenticationMethodsSupported + { + get { yield return ClientAuthenticationMethods.ClientSecretBasic; } + } + + /// + /// Tries to authenticate a client based on the 'Basic' authentication scheme. + /// This method extracts the Base64-encoded credentials from the 'Authorization' header of the request, decodes them, + /// and attempts to authenticate the client using the extracted credentials. + /// It adheres to the user-id and password format as outlined in RFC 7617, Section 2.1, where the first colon in the + /// credentials string separates the user-id (client ID) and the password (client secret). + /// + /// + /// The client request containing the authentication information in the 'Authorization' header. + /// + /// + /// A representing the asynchronous operation, which upon completion will yield the authenticated + /// or null if authentication fails. + /// If the 'Authorization' header is missing, malformed, or does not follow the Basic authentication scheme, + /// the method returns null. + /// + public async Task TryAuthenticateClientAsync(ClientRequest request) + { + if (request.AuthorizationHeader is not { Scheme: "Basic", Parameter: { } parameter }) + return null; + + var value = Encoding.ASCII.GetString(Convert.FromBase64String(parameter)); + + // According to RFC 7617: https://www.rfc-editor.org/rfc/rfc7617#section-2.1 + // the first colon in a user-pass string separates user-id and password from one another; + // text after the first colon is part of the password. + // User-ids containing colons cannot be encoded in user-pass strings. + var colonIndex = value.IndexOf(':'); + if (colonIndex < 0) + return null; + + return await TryAuthenticateAsync( + clientId: value[..colonIndex], + secret: value[(colonIndex + 1)..], + ClientAuthenticationMethods.ClientSecretBasic); + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretJwtAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretJwtAuthenticator.cs new file mode 100644 index 00000000..c2f72bc1 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretJwtAuthenticator.cs @@ -0,0 +1,98 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Authenticates client requests using the client_secret_jwt authentication method. +/// This method is used in scenarios where the client signs a JWT with its secret as a means of authentication. +/// +public class ClientSecretJwtAuthenticator : IClientAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// Logger for logging authentication process information and warnings. + /// Validator for JSON Web Tokens (JWT) used in client assertions. + /// Provider for retrieving client information, essential for validating client identity. + /// Provider for retrieving information about the current request, used in validating JWT claims like audience. + /// Service for hashing client secrets to compare with JWT signature. + public ClientSecretJwtAuthenticator( + ILogger logger, + IJsonWebTokenValidator tokenValidator, + IClientInfoProvider clientInfoProvider, + IRequestInfoProvider requestInfoProvider, + IHashService hashService) + { + _logger = logger; + _tokenValidator = tokenValidator; + _clientInfoProvider = clientInfoProvider; + _requestInfoProvider = requestInfoProvider; + _hashService = hashService; + } + + private readonly ILogger _logger; + private readonly IJsonWebTokenValidator _tokenValidator; + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IRequestInfoProvider _requestInfoProvider; + private readonly IHashService _hashService; + + /// + /// Specifies the client authentication method this authenticator supports, which is 'client_secret_jwt'. + /// This indicates that the authenticator handles client authentication using JSON Web Tokens (JWT) for + /// the client secret, as defined in the OpenID Connect specification. It involves using JWTs as + /// client credentials for authentication, where the JWT assertion is signed by the client's secret key. + /// + public IEnumerable ClientAuthenticationMethodsSupported + { + get { yield return ClientAuthenticationMethods.ClientSecretJwt; } + } + + /// + /// Asynchronously tries to authenticate a client request using the client_secret_jwt method. + /// Validates the JWT and checks if the client is authorized to use this authentication method. + /// + /// The client request to authenticate. + /// + /// A representing the asynchronous operation, which upon completion will yield the + /// authenticated , or null if the authentication is unsuccessful. + /// + public Task TryAuthenticateClientAsync(ClientRequest request) + { + // Example implementation here. You'll need to adjust the logic according to your actual JWT validation process, + // client information retrieval, and the specifics of how the client_secret_jwt is expected to work in your system. + // This might involve verifying the JWT signature using the client's secret, checking the issuer, subject, + // audience, and expiration of the JWT, and ensuring that the client is registered to use the client_secret_jwt method. + + //TODO implement client_secret_jwt + + return Task.FromResult(null); + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretPostAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretPostAuthenticator.cs new file mode 100644 index 00000000..71d28031 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/ClientSecretPostAuthenticator.cs @@ -0,0 +1,86 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Implements an authentication of a client request by extracting client credentials (client_id and client_secret) +/// from the request body. This approach is typically used in OAuth 2.0 client credential flows where the client submits +/// its credentials as part of the request body. +/// +public class ClientSecretPostAuthenticator : ClientSecretAuthenticator, IClientAuthenticator +{ + + /// + /// Initializes a new instance of the class. + /// + /// Logger for logging authentication-related information and errors. + /// The provider for client information retrieval. + /// The clock instance for time-related operations, such as checking secret expiration. + /// The client secret hasher used for verifying client secrets in a secure manner. + public ClientSecretPostAuthenticator( + ILogger logger, + IClientInfoProvider clientInfoProvider, + TimeProvider clock, + IHashService hashService) + : base(logger, clientInfoProvider, clock, hashService) + { + } + + /// + /// Specifies the client authentication method this authenticator supports, which is 'client_secret_post'. + /// This property indicates that the authenticator is designed to handle client authentication where the client secret + /// is sent in the request body parameters. It is a straightforward method for clients to authenticate with the + /// authorization server by including the client_id and client_secret in the body of the HTTP request. + /// + public IEnumerable ClientAuthenticationMethodsSupported + { + get { yield return ClientAuthenticationMethods.ClientSecretPost; } + } + + /// + /// Asynchronously tries to authenticate a client based on credentials (client_id and client_secret) provided in the request body. + /// The method delegates to with the extracted client_id and client_secret. + /// + /// The client request containing the client_id and client_secret for authentication. + /// + /// A representing the asynchronous operation, which upon completion will yield the authenticated + /// or null if authentication fails. + /// + public async Task TryAuthenticateClientAsync(ClientRequest request) + { + if (!request.ClientId.HasValue() || !request.ClientSecret.HasValue()) + return null; + + return await TryAuthenticateAsync( + request.ClientId, + request.ClientSecret, + ClientAuthenticationMethods.ClientSecretPost); + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/CompositeClientAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/CompositeClientAuthenticator.cs new file mode 100644 index 00000000..184bd41a --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/CompositeClientAuthenticator.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Aggregates multiple client authentication strategies into a single composite authenticator. +/// This class allows for attempting client authentication through a sequence of different +/// authentication methods, providing flexibility in supporting multiple authentication protocols. +/// +internal class CompositeClientAuthenticator : IClientAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// An array of implementations. + /// These authenticators are used in the order they are provided to attempt client authentication. + public CompositeClientAuthenticator(params IClientAuthenticator[] clientAuthenticators) + { + _clientAuthenticators = clientAuthenticators; + } + + private readonly IClientAuthenticator[] _clientAuthenticators; + + /// + /// Gets a collection of strings representing the client authentication methods supported by the implementation. + /// This can include methods such as client_secret_basic, client_secret_post, private_key_jwt, etc. + /// + public IEnumerable ClientAuthenticationMethodsSupported => + from authenticator in _clientAuthenticators + from method in authenticator.ClientAuthenticationMethodsSupported + select method; + + /// + /// Attempts to authenticate a client request by sequentially invoking each registered authenticator + /// until one succeeds or all fail. + /// + /// The to authenticate. This object contains details + /// about the request that may be used by authenticators to determine the client's identity. + /// + /// A representing the asynchronous operation. The task result is a + /// object representing the authenticated client if authentication is successful; otherwise, null. + /// + /// + /// This method provides a unified interface for client authentication, simplifying the process + /// of supporting multiple authentication mechanisms. It iterates through the provided authenticators + /// and returns the first successful authentication result, or null if no authenticator succeeds. + /// + public async Task TryAuthenticateClientAsync(ClientRequest request) + { + foreach (var clientAuthenticator in _clientAuthenticators) + { + var clientInfo = await clientAuthenticator.TryAuthenticateClientAsync(request); + if (clientInfo != null) + return clientInfo; + } + + return null; + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/IClientAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/IClientAuthenticator.cs new file mode 100644 index 00000000..c9346e49 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/IClientAuthenticator.cs @@ -0,0 +1,50 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Defines the interface for client authentication, supporting various authentication methods during OAuth flows. +/// +public interface IClientAuthenticator +{ + /// + /// Specifies the authentication methods supported by this authenticator. + /// This property should return a value that identify the authentication scheme + /// (e.g., "client_secret_basic", "private_key_jwt") supported by the implementer. + /// + IEnumerable ClientAuthenticationMethodsSupported { get; } + + /// + /// Attempts to authenticate a client based on the provided request. + /// It verifies the client's credentials and determines the authenticity of the client. + /// + /// The client request containing authentication information. + /// + /// A task that resolves to the authenticated if successful, + /// or null if authentication fails. + /// + Task TryAuthenticateClientAsync(ClientRequest request); +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/NoneClientAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/NoneClientAuthenticator.cs new file mode 100644 index 00000000..525375b4 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/NoneClientAuthenticator.cs @@ -0,0 +1,102 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Authenticates clients that are configured as public, without requiring client secrets. +/// +/// +/// This authenticator is designed for public clients where client secrets cannot be securely stored. It ensures that +/// only clients marked as public in the configuration are allowed to proceed without client authentication. +/// This approach is typically used in scenarios where the client application runs in an environment that +/// cannot securely maintain a secret, such as single-page applications or native mobile apps. +/// +public class NoneClientAuthenticator: IClientAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger for logging authentication events. + /// The provider for retrieving client information. + /// + /// This constructor prepares the authenticator with necessary dependencies for client lookup and logging. + /// + public NoneClientAuthenticator( + ILogger logger, + IClientInfoProvider clientInfoProvider) + { + _logger = logger; + _clientInfoProvider = clientInfoProvider; + } + + private readonly ILogger _logger; + private readonly IClientInfoProvider _clientInfoProvider; + + /// + /// Indicates the client authentication method supported by this authenticator. + /// For this authenticator, no client authentication is required, aligning with scenarios where + /// client authentication is deemed unnecessary or where anonymous access is permitted. + /// + public IEnumerable ClientAuthenticationMethodsSupported + { + get { yield return ClientAuthenticationMethods.None; } + } + + /// + /// Attempts to authenticate a client based solely on its ID, without requiring a client secret. + /// + /// The client request containing the client's ID. + /// A task that represents the asynchronous operation, returning the authenticated + /// if successful, or null if authentication fails. + /// + /// This method is suitable for public clients where a secret is not issued or cannot be securely stored. + /// It verifies the existence of the client and ensures it is marked as a public client in the configuration. + /// Clients not meeting these criteria are not authenticated. + /// + public async Task TryAuthenticateClientAsync(ClientRequest request) + { + var clientId = request.ClientId; + if (!clientId.HasValue()) + return null; + + var client = await _clientInfoProvider.TryFindClientAsync(clientId).WithLicenseCheck(); + if (client == null) + { + _logger.LogDebug("Client authentication failed: Client information with id {ClientId} is missing", clientId); + return null; + } + + if (client.ClientType == ClientType.Public) + return client; + + return null; + + } +} diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs new file mode 100644 index 00000000..930db230 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs @@ -0,0 +1,260 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens.Revocation; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using Microsoft.Extensions.Logging; +using static Abblix.Oidc.Server.Model.ClientRequest.Parameters; +using JsonWebKey = Abblix.Jwt.JsonWebKey; + +namespace Abblix.Oidc.Server.Features.ClientAuthentication; + +/// +/// Authenticates client requests using the Private Key JWT authentication method. +/// This method is typically used in scenarios where clients can securely hold a private key and +/// authenticate by providing a JWT signed with that key. +/// +public class PrivateKeyJwtAuthenticator : IClientAuthenticator +{ + /// + /// Initializes a new instance of the class. + /// + /// Logger for logging authentication process information and warnings. + /// Validator for JSON Web Tokens (JWT) used in client assertions. + /// Provider for retrieving client information, + /// essential for validating client identity. + /// Provider for retrieving information about the current request, + /// used in validating JWT claims like audience. + /// Registry for managing the status of tokens, such as marking them used. + /// Provider for retrieving client JSON Web Key Sets (JWKS), + /// necessary for validating JWT signatures. + public PrivateKeyJwtAuthenticator( + ILogger logger, + IJsonWebTokenValidator tokenValidator, + IClientInfoProvider clientInfoProvider, + IRequestInfoProvider requestInfoProvider, + ITokenRegistry tokenRegistry, + IClientKeysProvider clientJwksProvider) + { + _logger = logger; + _tokenValidator = tokenValidator; + _clientInfoProvider = clientInfoProvider; + _requestInfoProvider = requestInfoProvider; + _tokenRegistry = tokenRegistry; + _clientJwksProvider = clientJwksProvider; + } + + private readonly ILogger _logger; + private readonly IJsonWebTokenValidator _tokenValidator; + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IRequestInfoProvider _requestInfoProvider; + private readonly ITokenRegistry _tokenRegistry; + private readonly IClientKeysProvider _clientJwksProvider; + + /// + /// Indicates the client authentication method supported by this authenticator. + /// This method utilizes private keys and JSON Web Tokens (JWT) for client authentication, + /// allowing clients to assert their identity through the use of asymmetric key cryptography. + /// It is designed for environments where the client can securely hold a private key. + /// + public IEnumerable ClientAuthenticationMethodsSupported + { + get { yield return ClientAuthenticationMethods.PrivateKeyJwt; } + } + + /// + /// Asynchronously tries to authenticate a client request using the Private Key JWT method. + /// Validates the JWT and checks if the client is authorized to use this authentication method. + /// + /// The client request to authenticate. + /// + /// A representing the asynchronous operation, which upon completion will yield the + /// authenticated , or null if the authentication is unsuccessful. + /// + public async Task TryAuthenticateClientAsync(ClientRequest request) + { + if (request.ClientAssertionType != ClientAssertionTypes.JwtBearer) + return null; + + if (!request.ClientAssertion.HasValue()) + { + _logger.LogWarning($"{ClientAssertionType} is '{ClientAssertionTypes.JwtBearer}', but {ClientAssertion} is empty"); + return null; + } + + var issuerValidator = new IssuerValidator(_clientInfoProvider, _clientJwksProvider); + + var result = await _tokenValidator.ValidateAsync( + request.ClientAssertion, + new ValidationParameters + { + ValidateAudience = ValidateAudience, + ValidateIssuer = issuerValidator.ValidateIssuer, + ResolveIssuerSigningKeys = issuerValidator.ResolveIssuerSigningKeys, + }); + + JsonWebToken token; + switch (result) + { + case ValidJsonWebToken validToken: + token = validToken.Token; + break; + + case JwtValidationError error: + _logger.LogWarning("Invalid PrivateKeyJwt: {@Error}", error); + return null; + + default: + throw new UnexpectedTypeException(nameof(result), result.GetType()); + } + + string? subject; + try + { + subject = token.Payload.Subject; + } + catch (InvalidOperationException ex) + { + _logger.LogWarning("Invalid PrivateKeyJwt: {Message}", ex.Message); + return null; + } + + var issuer = token.Payload.Issuer; + if (issuer == null || subject == null || issuer != subject) + { + _logger.LogWarning( + "Invalid PrivateKeyJwt: iss is \'{Issuer}\', but sub is {Subject}", + issuer, subject); + + return null; + } + + if (token is { Payload: { JwtId: { } jwtId, ExpiresAt: {} expiresAt } }) + { + await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Used, expiresAt); + } + + return issuerValidator.ClientInfo; + } + + private Task ValidateAudience(IEnumerable audiences) + { + var requestUri = _requestInfoProvider.RequestUri; + var result = audiences.Contains(requestUri); + if (!result) + { + _logger.LogWarning( + "Audience validation failed, token audiences: {@Audiences}, actual requestUri: {RequestUri}", + audiences, requestUri); + } + + return Task.FromResult(result); + } + + /// + /// Validates issuers and resolves their signing keys to ensure secure token validation and authentication processes. + /// + /// + /// This class plays a critical role in the security infrastructure by validating token issuers against known client + /// information and resolving the issuer's signing keys for JWT validation. It supports the secure and reliable + /// verification of JWTs, which are central to authentication and authorization mechanisms. + /// + private class IssuerValidator + { + /// + /// Initializes a new instance of the class. + /// + /// Provides access to client information. + /// Provides access to the client's JSON Web Keys (JWKs). + public IssuerValidator(IClientInfoProvider clientInfoProvider, IClientKeysProvider clientJwksProvider) + { + _clientInfoProvider = clientInfoProvider; + _clientJwksProvider = clientJwksProvider; + } + + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IClientKeysProvider _clientJwksProvider; + + /// + /// The client information if the issuer has been successfully validated. + /// + public ClientInfo? ClientInfo { get; private set; } + + /// + /// Validates the issuer by attempting to match it with known client information. If the issuer has already been + /// validated and stored in , the method checks if the provided issuer matches + /// the stored client ID. Throws an exception if a mismatch is detected, ensuring that + /// the validator is not misused to validate issuers against different client information. + /// + /// The issuer value to validate. + /// A task that represents the asynchronous operation. The task result contains a boolean + /// indicating whether the issuer has been successfully validated. If the issuer is already validated and matches + /// the stored client information, the validation is considered successful without further checks. + /// Thrown if attempting to validate a different issuer than the one + /// associated with already stored client information. + public async Task ValidateIssuer(string issuer) + { + switch (ClientInfo) + { + case { ClientId: var clientId } when issuer != clientId: + throw new InvalidOperationException( + $"Trying to validate issuer {issuer}, but already has info about client {clientId}"); + + case not null: + return true; + + default: + var clientInfo = await _clientInfoProvider.TryFindClientAsync(issuer).WithLicenseCheck(); + if (clientInfo is not { TokenEndpointAuthMethod: ClientAuthenticationMethods.PrivateKeyJwt }) + { + return false; + } + + ClientInfo = clientInfo; + return true; + } + } + + /// + /// Asynchronously resolves the signing keys for a validated issuer's JWTs. + /// + /// The issuer URL whose signing keys are to be resolved. + /// An asynchronous stream of objects representing the issuer's + /// signing keys. + public async IAsyncEnumerable ResolveIssuerSigningKeys(string issuer) + { + if (!await ValidateIssuer(issuer)) + yield break; + + await foreach (var key in _clientJwksProvider.GetSigningKeys(ClientInfo.NotNull(nameof(ClientInfo)))) + yield return key; + } + } +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/ClientInfo.cs b/Abblix.Oidc.Server/Features/ClientInformation/ClientInfo.cs new file mode 100644 index 00000000..fc54f297 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/ClientInfo.cs @@ -0,0 +1,208 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Constants; + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Contains information about a client in an OAuth2/OpenID Connect context. +/// +/// +/// This record encapsulates the details necessary to identify and configure the behavior of a client application +/// within an OAuth2 or OpenID Connect framework. It includes identifiers, secrets, and configuration options +/// that dictate how the client interacts with the authorization server and is authenticated or authorized during +/// the token issuance process. +/// +public record ClientInfo(string ClientId) +{ + /// + /// Identifies the client's unique identifier as recognized by the authorization server. + /// It is used in various OAuth 2.0 and OpenID Connect flows to represent the client application. + /// + public string ClientId { get; set; } = ClientId; + + /// + /// Classifies the client based on its ability to securely maintain a client secret. + /// This classification influences the authorization flow and token endpoint authentication method that + /// the client can utilize. Public clients, such as mobile or desktop applications, cannot securely store secrets, + /// while confidential clients, like server-side web applications, can. + /// + public ClientType ClientType { get; set; } = ClientType.Public; + + /// + /// A collection of secrets associated with the client, used for authenticating the client to the authorization server. + /// Multiple secrets can be provided for added security. + /// + public ClientSecret[]? ClientSecrets { get; set; } + + /// + /// Specifies the URIs where the user-agent can be redirected after authorization. + /// These URIs must be pre-registered and match the redirect URI provided in the authorization request. + /// + public Uri[] RedirectUris { get; set; } = Array.Empty(); + + /// + /// Specifies the URIs where the user-agent can be redirected after logging out from the client application. + /// This allows for a seamless user experience upon logout. + /// + public Uri[] PostLogoutRedirectUris { get; set; } = Array.Empty(); + + /// + /// Indicates whether the client is to use Proof Key for Code Exchange (PKCE) in the authorization code flow, + /// enhancing security for public clients. + /// + public bool PkceRequired { get; set; } = false; + + /// + /// Indicates if the client is allowed to use the "plain" method for PKCE. + /// It is recommended to use stronger methods like "S256" for enhanced security. + /// + public bool PlainPkceAllowed { get; set; } = false; + + /// + /// The validity period of an authorization code issued to this client. + /// Shorter durations are recommended for higher security. + /// + public TimeSpan AuthorizationCodeExpiresIn { get; set; } = TimeSpan.FromMinutes(5); + + /// + /// Specifies the lifetime of access tokens issued to this client. + /// Shorter access token lifetimes reduce the risk of token leakage. + /// + public TimeSpan AccessTokenExpiresIn { get; set; } = TimeSpan.FromHours(1); + + /// + /// Configures the behavior and properties of refresh tokens issued to this client, + /// such as their expiration and renewal policies. + /// + public RefreshTokenOptions RefreshToken { get; set; } = new(); + + /// + /// Determines the validity period of identity tokens issued to this client. + /// Shorter durations enhance security by reducing the window of misuse. + /// + public TimeSpan IdentityTokenExpiresIn { get; set; } = TimeSpan.FromMinutes(5); + + /// + /// Options for configuring front-channel logout behavior, allowing the client to participate in logout requests + /// initiated by other clients. + /// + public FrontChannelLogoutOptions? FrontChannelLogout { get; set; } + + /// + /// Options for configuring back-channel logout behavior, enabling the server to directly notify + /// the client of logout events. + /// + public BackChannelLogoutOptions? BackChannelLogout { get; set; } + + /// + /// Defines the response types that the client is permitted to use. + /// This controls how tokens are issued in response to an authorization request. + /// + public string[][] AllowedResponseTypes { get; set; } = { new[] { ResponseTypes.Code } }; + + /// + /// Specifies the grant types the client is authorized to use when obtaining tokens from the token endpoint. + /// + public string[] AllowedGrantTypes { get; set; } = { GrantTypes.AuthorizationCode }; + + /// + /// Allows the client to request tokens that enable access to the user's resources while they are offline. + /// + public bool OfflineAccessAllowed { get; set; } = false; + + /// + /// The set of JSON Web Keys used by the client, typically for signing request objects and decrypting + /// identity tokens or encrypted user information. + /// + public JsonWebKeySet? Jwks { get; set; } + + /// + /// The publicly accessible URL where the client's JSON Web Key Set (JWKS) can be retrieved. + /// + public Uri? JwksUri { get; set; } + + /// + /// Specifies the algorithm that must be used for signing identity token responses issued to this client. + /// + public string IdentityTokenSignedResponseAlgorithm { get; set; } = SigningAlgorithms.RS256; + + /// + /// Controls whether claims about the authenticated user are included directly in the identity token + /// instead of being obtained separately via the UserInfo endpoint. + /// + public bool ForceUserClaimsInIdentityToken { get; set; } = false; + + /// + /// Describes how the client authenticates to the token endpoint. + /// Common methods include client_secret_basic and client_secret_post. + /// + public string TokenEndpointAuthMethod { get; set; } = ClientAuthenticationMethods.ClientSecretBasic; + + /// + /// Determines the algorithm used for signing responses from the UserInfo endpoint. + /// This can enhance the security of transmitted user information. + /// + public string UserInfoSignedResponseAlgorithm { get; set; } = SigningAlgorithms.None; + + /// + /// A URL pointing to the client's policy documentation, providing transparency on how user data + /// is handled and protected. + /// + public Uri? PolicyUri { get; set; } + + /// + /// A URL pointing to the client's terms of service, outlining the legal agreement between the user + /// and the service provider. + /// + public Uri? TermsOfServiceUri { get; set; } + + /// + /// A URL pointing to an image file representing the client's logo, which can be displayed in user interfaces + /// during authorization. + /// + public Uri? LogoUri { get; set; } + + /// + /// A URI that allows third-party sites to initiate a login by the client, facilitating integrations and + /// single sign-on scenarios. + /// + public Uri? InitiateLoginUri { get; set; } + + /// + /// Specifies the subject identifier type requested by the client. This influences how the authorization server + /// represents the authenticated user's identity to the client, affecting privacy and uniqueness across different + /// clients. Common types include "public" and "pairwise". + /// + public string? SubjectType { get; set; } = SubjectTypes.Public; + + /// + /// Used in conjunction with pairwise subject identifiers to calculate the subject value returned to the client. + /// This field is particularly relevant for ensuring user privacy by providing a different subject identifier + /// to each client, even if it's the same end-user. It typically contains a URL or a unique identifier + /// representing the client's sector. + /// + public string? SectorIdentifier { get; set; } +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/ClientInfoStorage.cs b/Abblix.Oidc.Server/Features/ClientInformation/ClientInfoStorage.cs new file mode 100644 index 00000000..aa168ff9 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/ClientInfoStorage.cs @@ -0,0 +1,81 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Manages the storage and retrieval of client information for OpenID Connect (OIDC) flows. +/// This class provides methods to access client configurations stored in . +/// +internal class ClientInfoStorage : IClientInfoProvider, IClientInfoManager +{ + /// + /// Initializes a new instance of the class, loading client configurations + /// from the provided . + /// + /// The OIDC options containing client configurations. + public ClientInfoStorage(IOptions options) + { + _clients = options.Value.Clients.ToDictionary(client => client.ClientId, StringComparer.OrdinalIgnoreCase); + } + + private readonly Dictionary _clients; + + /// + /// Asynchronously searches for a client by its identifier. + /// + /// The unique identifier of the client to find. + /// + /// A task that, when completed successfully, returns a object representing + /// the client if found; otherwise, null. + /// + public Task TryFindClientAsync(string clientId) + { + ArgumentNullException.ThrowIfNull(clientId, nameof(clientId)); + return Task.FromResult(_clients.GetValueOrDefault(clientId)); + } + + /// + /// Adds the provided client information to the storage asynchronously. + /// + /// The client information to be added. + /// A task that represents the asynchronous operation of adding a client. + public Task AddClientAsync(ClientInfo clientInfo) + { + _clients.Add(clientInfo.ClientId, clientInfo); + return Task.CompletedTask; + } + + /// + /// Removes the client identified by the given client ID from the storage asynchronously. + /// + /// The unique identifier of the client to be removed. + /// A task that represents the asynchronous operation of removing a client. + public Task RemoveClientAsync(string clientId) + { + _clients.Remove(clientId); + return Task.CompletedTask; + } +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/ClientKeysProvider.cs b/Abblix.Oidc.Server/Features/ClientInformation/ClientKeysProvider.cs new file mode 100644 index 00000000..ef3385af --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/ClientKeysProvider.cs @@ -0,0 +1,111 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Json; +using Abblix.Jwt; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Facilitates the retrieval of JSON Web Keys (JWKs) for cryptographic operations, including encryption and signing. +/// This provider supports fetching keys from a client's JSON Web Key Set (JWKS) URL or directly from the client configuration. +/// +public class ClientKeysProvider : IClientKeysProvider +{ + /// + /// Initializes a new instance of the class. + /// + /// Logger for capturing any operational logs. + /// Factory for creating instances of used to fetch JWKS from remote URLs. + public ClientKeysProvider( + ILogger logger, + IHttpClientFactory httpClientFactory) + { + _logger = logger; + _httpClientFactory = httpClientFactory; + } + + private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + + /// + /// Retrieves the encryption keys associated with a specific client. + /// + /// Client information containing either JWKS or a JWKS URI. + /// A collection of encryption keys as an asynchronous enumerable. + public IAsyncEnumerable GetEncryptionKeys(ClientInfo clientInfo) + { + return GetKeys(clientInfo).WhereAsync(key => key.Usage == PublicKeyUsages.Encryption); + } + + /// + /// Retrieves the signing keys associated with a specific client. + /// + /// Client information containing either JWKS or a JWKS URI. + /// A collection of signing keys as an asynchronous enumerable. + public IAsyncEnumerable GetSigningKeys(ClientInfo clientInfo) + { + return GetKeys(clientInfo).WhereAsync(key => key.Usage == PublicKeyUsages.Signature); + } + + /// + /// Internally fetches keys from the client's JWKS or JWKS URI. + /// + /// The client information specifying where to find the JWKS. + /// An asynchronous enumerable of . + /// + /// This method attempts to retrieve keys directly from the client's configured JWKS. If a JWKS URI is provided, + /// it fetches the JWKS from the remote URI. Logs warnings if the retrieval process fails. + /// + private async IAsyncEnumerable GetKeys(ClientInfo clientInfo) + { + // Directly yield keys from configured JWKS + if (clientInfo.Jwks != null) + { + foreach (var key in clientInfo.Jwks.Keys) + yield return key; + } + + // Attempt to fetch keys from JWKS URI + var jwksUri = clientInfo.JwksUri; + if (jwksUri == null) + yield break; + + JsonWebKeySet? jwks = null; + try + { + jwks = await _httpClientFactory.CreateClient().GetFromJsonAsync(jwksUri); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unable to get JWKS from specified URI: {JwksUri}", jwksUri); + } + + if (jwks == null) + yield break; + + foreach (var key in jwks.Keys) + yield return key; + } +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/ClientSecret.cs b/Abblix.Oidc.Server/Features/ClientInformation/ClientSecret.cs new file mode 100644 index 00000000..ca81b10b --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/ClientSecret.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Encapsulates the details of a client secret used in OAuth2 and OpenID Connect authentication flows. +/// +/// +/// Client secrets are critical for the security of client applications, especially those that +/// authenticate in a server-side context. This record stores hashed versions of the secret +/// to enhance security by avoiding the storage of plain-text secrets. +/// +public record ClientSecret +{ + /// + /// The SHA-256 hash of the client secret. This property is used to securely store + /// and verify the secret without needing to store the plain text value. + /// + /// + /// The SHA-256 hash provides a secure way to handle client secrets, allowing + /// for their verification during the authentication process without risking exposure. + /// + public byte[]? Sha256Hash { get; init; } + + /// + /// The SHA-512 hash of the client secret. This property offers an additional layer + /// of security by using a stronger hashing algorithm compared to SHA-256. + /// + /// + /// SHA-512 hashes are more resistant to brute-force attacks due to their larger size + /// and complexity. This property is optional and can be used in systems requiring + /// heightened security measures. + /// + public byte[]? Sha512Hash { get; init; } + + /// + /// The expiration date and time for the client secret. Secrets past this date are considered + /// invalid and cannot be used for authentication. + /// + /// + /// Setting an expiration date for client secrets is a best practice that helps mitigate + /// the risk of secret compromise over time. It encourages regular rotation of secrets + /// to maintain the security integrity of client applications. + /// + public DateTimeOffset? ExpiresAt { get; init; } +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoManager.cs b/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoManager.cs new file mode 100644 index 00000000..c96622d0 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoManager.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Defines operations for managing the lifecycle and information of OAuth 2.0 clients in a storage system. +/// +/// +/// Implementations of this interface are responsible for adding, updating, and removing client information, +/// supporting dynamic client registration and management in OAuth 2.0 and OpenID Connect environments. +/// +public interface IClientInfoManager +{ + /// + /// Asynchronously adds a new client and its corresponding information to the storage system. + /// + /// The detailed information about the client to be added. + /// A task representing the asynchronous operation, indicating the completion of the addition process. + /// + /// This operation typically involves persisting the to a database or another form of storage, + /// making the client available for OAuth 2.0 and OpenID Connect authentication and authorization processes. + /// + Task AddClientAsync(ClientInfo clientInfo); + + /// + /// Asynchronously removes an existing client and its information from the storage system. + /// + /// The unique identifier of the client to be removed. + /// A task representing the asynchronous operation, indicating the completion of the removal process. + /// + /// The removal process is critical for maintaining the integrity and security of the client registration system, + /// allowing administrators to effectively manage the lifecycle of client applications. + /// + Task RemoveClientAsync(string clientId); +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoProvider.cs b/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoProvider.cs new file mode 100644 index 00000000..bcdd8663 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/IClientInfoProvider.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Provides access to OAuth 2.0 client information, enabling the retrieval of client details by client ID. +/// +/// +/// This interface is crucial for supporting OAuth 2.0 and OpenID Connect operations, such as token issuance +/// and validation, by allowing the system to retrieve the configuration and settings for registered clients. +/// It abstracts the underlying storage mechanism, whether it's a database, in-memory collection, or an external service. +/// +public interface IClientInfoProvider +{ + /// + /// Asynchronously attempts to find a client's information using its unique identifier. + /// + /// The unique identifier of the client whose information is being requested. + /// + /// A task that represents the asynchronous operation, resulting in the client's information if found; + /// otherwise, null. This allows for non-blocking queries to the underlying client information storage. + /// + /// + /// This method facilitates dynamic client management by enabling on-demand lookup of client configurations + /// during OAuth 2.0 and OpenID Connect flows, supporting scenarios such as dynamic client registration + /// and configuration updates. + /// + Task TryFindClientAsync(string clientId); +} diff --git a/Abblix.Oidc.Server/Features/ClientInformation/IClientKeysProvider.cs b/Abblix.Oidc.Server/Features/ClientInformation/IClientKeysProvider.cs new file mode 100644 index 00000000..a280ea69 --- /dev/null +++ b/Abblix.Oidc.Server/Features/ClientInformation/IClientKeysProvider.cs @@ -0,0 +1,61 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + +namespace Abblix.Oidc.Server.Features.ClientInformation; + +/// +/// Provides access to cryptographic keys for OpenID Connect clients, supporting token encryption and signature validation. +/// +/// +/// This interface serves as a bridge between OAuth 2.0/OpenID Connect clients and the cryptographic keys necessary +/// for securing JWT tokens. It enables dynamic retrieval of encryption and signing keys which can be used for +/// token encryption, signature, and validation processes. This approach supports scenarios where keys are rotated +/// or updated without requiring service restarts or manual intervention. +/// +public interface IClientKeysProvider +{ + /// + /// Retrieves the set of encryption keys associated with a given client, allowing the service to encrypt + /// JWT tokens or other sensitive information intended for that client. + /// + /// The client's information, used to identify the correct set of encryption keys. + /// An asynchronous stream () of , providing access to each key. + /// + /// This method is essential for services that issue encrypted tokens or need to securely communicate with clients, + /// ensuring that only the intended recipient can decrypt and access the transmitted information. + /// + IAsyncEnumerable GetEncryptionKeys(ClientInfo clientInfo); + + /// + /// Retrieves the set of signing keys associated with a given client, enabling the service to validate + /// signatures on JWT tokens or other signed payloads originating from that client. + /// + /// The client's information, used to identify the correct set of signing keys. + /// An asynchronous stream () of , providing access to each key. + /// + /// This method supports secure client-server interactions by enabling the service to verify the authenticity + /// of incoming signed data, ensuring it was not tampered with and was indeed issued by the claiming client. + /// + IAsyncEnumerable GetSigningKeys(ClientInfo clientInfo); +} diff --git a/Abblix.Oidc.Server/Features/Consents/IConsentService.cs b/Abblix.Oidc.Server/Features/Consents/IConsentService.cs new file mode 100644 index 00000000..ee23fd83 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Consents/IConsentService.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Consents; + +/// +/// Provides methods to determine whether user consent is to proceed with authentication. +/// +public interface IConsentService +{ + /// + /// Checks if consent is for the given authorization request and authentication session. + /// + /// The authorization request. + /// The authentication session. + /// True if consent is required, false otherwise. + Task IsConsentRequired(ValidAuthorizationRequest request, AuthSession authSession); +} diff --git a/Abblix.Oidc.Server/Features/Consents/NullConsentService.cs b/Abblix.Oidc.Server/Features/Consents/NullConsentService.cs new file mode 100644 index 00000000..4d242d4b --- /dev/null +++ b/Abblix.Oidc.Server/Features/Consents/NullConsentService.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Consents; + +/// +/// Implements the very basic consent service not requiring any consents. +/// +/// +/// Replace it with your own implementation in case you need to require a consent from a user. +/// +public class NullConsentService : IConsentService +{ + public Task IsConsentRequired(ValidAuthorizationRequest request, AuthSession authSession) + => Task.FromResult(false); +} diff --git a/Abblix.Oidc.Server/Features/Hashing/HashAlgorithm.cs b/Abblix.Oidc.Server/Features/Hashing/HashAlgorithm.cs new file mode 100644 index 00000000..9a4b2de3 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Hashing/HashAlgorithm.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Hashing; + +/// +/// Specifies the hash algorithms supported for hashing operations. +/// +public enum HashAlgorithm +{ + /// + /// Represents the SHA-256 hash algorithm. + /// SHA-256 (Secure Hash Algorithm 256-bit) is a cryptographic hash function + /// that produces a 256-bit hash value, widely used for data integrity verification. + /// + Sha256, + + /// + /// Represents the SHA-512 hash algorithm. + /// SHA-512 (Secure Hash Algorithm 512-bit) is a cryptographic hash function + /// that produces a 512-bit hash value. It is used in various security applications + /// and protocols, including TLS and SSL, PGP, SSH, and IPsec. + /// + Sha512, +} diff --git a/Abblix.Oidc.Server/Features/Hashing/HashService.cs b/Abblix.Oidc.Server/Features/Hashing/HashService.cs new file mode 100644 index 00000000..3a3b12b8 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Hashing/HashService.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Text; + +namespace Abblix.Oidc.Server.Features.Hashing; + +/// +/// Provides hashing functionality for various types of data, including but not limited to client secrets, +/// in OAuth 2.0 and OpenID Connect authentication flows. +/// This class supports SHA-256 and SHA-512 hashing algorithms to securely hash data. +/// +/// +/// Hashing data, especially secrets, enhances privacy and security by ensuring that only a hashed version +/// of the data is stored. In the event of a data breach, attackers cannot access the actual data, such as +/// client secrets, thereby reducing the risk of exploitation. +/// +public class HashService : IHashService +{ + /// + /// Computes a hash for the provided data using the specified hash algorithm. + /// + /// The hash algorithm to use (e.g., SHA-256 or SHA-512). + /// The data to hash. + /// A byte array containing the hash of the data. + /// Thrown when the specified hash algorithm is not supported. + public byte[] Sha(HashAlgorithm algorithm, string data) + { + var bytes = Encoding.ASCII.GetBytes(data); + return algorithm switch + { + HashAlgorithm.Sha256 => SHA256.HashData(bytes), + HashAlgorithm.Sha512 => SHA512.HashData(bytes), + + _ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, + $"The hash algorithm {algorithm} is not supported"), + }; + } +} diff --git a/Abblix.Oidc.Server/Features/Hashing/IHashService.cs b/Abblix.Oidc.Server/Features/Hashing/IHashService.cs new file mode 100644 index 00000000..00b48326 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Hashing/IHashService.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Hashing; + +/// +/// Offers hashing functionality for data using various Secure Hash Algorithms (SHA). +/// This service is essential for securely storing and comparing sensitive information +/// like passwords or client secrets without exposing the actual values. +/// +public interface IHashService +{ + /// + /// Generates a hash for the specified data using a chosen SHA algorithm. + /// This method provides a way to securely hash sensitive data, such as secrets + /// or passwords, ensuring that the original data cannot be easily derived from the hash. + /// + /// Specifies the SHA algorithm to use for hashing, such as SHA-256 or SHA-512. + /// The data to hash. Typically, this is sensitive information that needs secure handling. + /// A byte array containing the hash of the input data. + /// + /// It is crucial to select an appropriate SHA algorithm based on security requirements and performance considerations. + /// The hash output is ideal for verifying data integrity and authenticating users or clients without storing or transmitting + /// sensitive plain-text data. + /// + byte[] Sha(HashAlgorithm algorithm, string data); +} diff --git a/Abblix.Oidc.Server/Features/Issuer/IIssuerProvider.cs b/Abblix.Oidc.Server/Features/Issuer/IIssuerProvider.cs new file mode 100644 index 00000000..185d644b --- /dev/null +++ b/Abblix.Oidc.Server/Features/Issuer/IIssuerProvider.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Issuer; + +/// +/// Provides a mechanism to retrieve the issuer identifier for the OpenID Connect provider. +/// The issuer identifier is a fundamental part of the token validation process, +/// as it indicates the origin of the token. +/// +public interface IIssuerProvider +{ + /// + /// Retrieves the issuer identifier that represents the OpenID Connect provider. + /// This identifier is used in various OpenID Connect responses and tokens to + /// ensure the identity of the issuing server. + /// + /// A string representing the issuer identifier. + string GetIssuer(); +} diff --git a/Abblix.Oidc.Server/Features/Issuer/PreconfiguredIssuerProvider.cs b/Abblix.Oidc.Server/Features/Issuer/PreconfiguredIssuerProvider.cs new file mode 100644 index 00000000..e9ac6738 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Issuer/PreconfiguredIssuerProvider.cs @@ -0,0 +1,51 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Utils; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.Issuer; + +/// +/// Provides the issuer identifier for tokens based on preconfigured options in the OpenID Connect (OIDC) configuration. +/// This provider retrieves the issuer identifier from the OIDC options, making it ideal for scenarios where the issuer +/// needs to be consistent and predefined, such as environments with multiple hosts. +/// +internal class PreconfiguredIssuerProvider : IIssuerProvider +{ + public PreconfiguredIssuerProvider(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Retrieves the issuer identifier from the OIDC options. + /// + /// The identifier of the issuer as configured in OIDC options. + /// + /// Thrown if the issuer identifier is not configured in OIDC options. + /// + public string GetIssuer() => _options.Value.Issuer.NotNull(nameof(OidcOptions.Issuer)); +} diff --git a/Abblix.Oidc.Server/Features/Issuer/RequestBasedIssuerProvider.cs b/Abblix.Oidc.Server/Features/Issuer/RequestBasedIssuerProvider.cs new file mode 100644 index 00000000..a6f63552 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Issuer/RequestBasedIssuerProvider.cs @@ -0,0 +1,50 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Interfaces; + +namespace Abblix.Oidc.Server.Features.Issuer; + +/// +/// Dynamically determines the issuer identifier based on the incoming HTTP request. +/// This approach allows the issuer identifier to reflect the actual request's context, +/// accommodating scenarios like varying host names or different environments. +/// +internal class RequestBasedIssuerProvider : IIssuerProvider +{ + /// + /// Initializes a new instance of the class. + /// + /// The provider that supplies information about the current HTTP request. + public RequestBasedIssuerProvider(IRequestInfoProvider requestInfoProvider) + { + _requestInfoProvider = requestInfoProvider; + } + + private readonly IRequestInfoProvider _requestInfoProvider; + + /// + /// Retrieves the issuer identifier based on the current HTTP request. + /// + /// The issuer identifier, constructed from the request's context. + public string GetIssuer() => _requestInfoProvider.ApplicationUri; +} diff --git a/Abblix.Oidc.Server/Features/Licensing/AggregationExtensions.cs b/Abblix.Oidc.Server/Features/Licensing/AggregationExtensions.cs new file mode 100644 index 00000000..5b3ba64a --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/AggregationExtensions.cs @@ -0,0 +1,112 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Provides extension methods for aggregating values from objects based on specific comparable properties. +/// +public static class AggregationExtensions +{ + /// + /// Determines the greater of two nullable values, treating null as positive infinity. + /// + /// The type of the values being compared, constrained to value types that implement + /// . + /// The first nullable value to compare. + /// The second nullable value to compare. + /// The greater of the two values if at least one is non-null; otherwise, null. If both values are + /// non-null, the method returns null only if the is null, indicating it is + /// considered as positive infinity. + /// + /// This method is useful in scenarios where you're aggregating a collection of nullable values and consider + /// the absence of a value (null) as the highest possible value, allowing for custom maximum value logic. + /// + public static T? Greater(this T? accumulatorValue, T? currentValue) + where T : struct, IComparable + { + if (currentValue.HasValue) + { + if (accumulatorValue.HasValue && accumulatorValue.Value.CompareTo(currentValue.Value) < 0) + { + return currentValue; + } + } + else if (accumulatorValue.HasValue) + { + return null; + } + + return accumulatorValue; + } + + /// + /// Determines the lesser of two nullable values, treating null as negative infinity. + /// + /// The type of the values being compared, constrained to value types that implement + /// . + /// The first nullable value to compare. + /// The second nullable value to compare. + /// The lesser of the two values if at least one is non-null; otherwise, null. If both values are non-null, + /// the method returns null only if the is null, indicating it is considered + /// as negative infinity. + /// + /// This method supports scenarios requiring aggregation of a series of nullable values where the absence of + /// a value (null) is interpreted as the lowest possible value, enabling custom minimum value logic. + /// + public static T? Lesser(this T? currentValue, T? accumulatorValue) + where T : struct, IComparable + { + if (currentValue.HasValue) + { + if (!accumulatorValue.HasValue || currentValue.Value.CompareTo(accumulatorValue.Value) < 0) + { + return currentValue; + } + } + + return accumulatorValue; + } + + /// + /// Combines the elements of two hash sets into a single set, including all unique elements from both. + /// + /// The type of elements in the hash sets. + /// The first hash set. + /// The second hash set to combine with the first. + /// A new hash set containing all unique elements from both input sets. If both inputs are null, returns null. + /// + /// This method provides a convenient way to merge two sets of elements, ensuring that the result contains + /// all distinct elements from both sets. It is particularly useful for combining collections of unique items + /// without duplicating any elements. + /// + public static HashSet? Join(this HashSet? accumulator, HashSet? current) + { + return (accumulator, current) switch + { + (null, null) => null, + (not null, null) => accumulator, + (null, not null) => current, + (not null, not null) => accumulator.Concat(current).ToHashSet(accumulator.Comparer), + }; + } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/ILicenseJwtProvider.cs b/Abblix.Oidc.Server/Features/Licensing/ILicenseJwtProvider.cs new file mode 100644 index 00000000..4d871177 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/ILicenseJwtProvider.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Defines a provider for accessing the license JSON Web Token (JWT) used in OIDC service configuration. +/// +/// +/// This interface abstracts the mechanism for retrieving the license JWT, which is essential for validating the +/// configuration and operational scope of the OIDC service based on licensing terms. Implementations of this interface +/// should ensure secure and efficient access to the license JWT, typically stored in service configuration settings. +/// +public interface ILicenseJwtProvider +{ + /// + /// Asynchronously gets the license JWT string. + /// + /// + /// A task representing the asynchronous operation, which upon completion contains the license JWT used for + /// configuration and licensing validation of the OIDC service. + /// + IAsyncEnumerable? GetLicenseJwtAsync(); +} diff --git a/Abblix.Oidc.Server/Features/Licensing/License.cs b/Abblix.Oidc.Server/Features/Licensing/License.cs new file mode 100644 index 00000000..8f3df6df --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/License.cs @@ -0,0 +1,97 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Represents the licensing constraints applied to the application, including limits on the number of clients and issuers, +/// as well as the validity period of the license. +/// +/// +/// This record is central to defining and enforcing operational limits and licensing terms within the application. It +/// supports not only quantitative restrictions, such as the number of clients and issuers, but also temporal constraints, +/// specifying when the license is valid and providing a grace period beyond the expiration date. +/// +/// Properties: +/// - and impose limits on the number of clients and issuers +/// that can interact with the application, ensuring compliance with the licensing agreement. +/// - specifies which issuers are recognized as valid sources of tokens or claims, adding +/// an additional layer of security and compliance. +/// - and define the time frame during which the license is considered +/// valid, allowing for precise control over the license's lifecycle. +/// - offers flexibility by defining a period after during which the +/// license constraints are still enforced, but the application may remain operational to account for renewal +/// processes. +/// +/// Together, these properties enable a robust and flexible approach to licensing, facilitating compliance, security, +/// and operational continuity. +/// +public record License +{ + /// + /// The maximum number of clients that are allowed to interact with the application under the current license. + /// + /// + /// This property specifies a limit on the number of unique client applications that can be registered or authenticated + /// by the application. It's a crucial aspect of licensing enforcement, ensuring that the application usage does not + /// exceed the terms agreed upon in the licensing contract. A value of null indicates that there is no limit on + /// the number of clients. + /// + /// When the number of unique clients exceeds this limit, the application should enforce the licensing terms by + /// restricting further client registrations or authentications, aligning with the compliance requirements. + /// + public int? ClientLimit { get; init; } + + /// + /// The maximum number of issuers that are recognized as valid by the application under the current license. + /// + /// + /// This property defines a cap on the number of distinct issuers from which the application will accept tokens or + /// claims. It plays a vital role in controlling access and ensuring that the application's interactions are within + /// the bounds set by its licensing terms. A null value for this property implies that there's no restriction + /// on the number of issuers. + /// + /// Exceeding this limit may require the application to implement measures that block tokens or claims issued by + /// additional issuers, thereby maintaining adherence to the licensing agreement. + /// + public int? IssuerLimit { get; init; } + + /// + /// An array of strings representing the issuers that are considered valid for this license. + /// + public HashSet? ValidIssuers { get; init; } + + /// + /// The date and time before which the license is not valid. + /// + public DateTimeOffset? NotBefore { get; init; } + + /// + /// The expiration date and time of the license. + /// + public DateTimeOffset? ExpiresAt { get; init; } + + /// + /// An optional grace period after the expiration date during which the license conditions are still considered valid. + /// + public DateTimeOffset? GracePeriod { get; init; } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseChecker.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseChecker.cs new file mode 100644 index 00000000..8207cdf9 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseChecker.cs @@ -0,0 +1,146 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Collections.Concurrent; +using Abblix.Oidc.Server.Features.ClientInformation; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Manages and enforces licensing constraints on clients and issuers within the application, ensuring compliance +/// with defined licensing terms. +/// +/// +/// This class dynamically validates the number of clients and issuers against the licensing terms, +/// logging warnings or errors when the application +/// operates beyond these constraints. It supports real-time updates to the license, +/// allowing the application to adjust to new licenses dynamically. +/// +public static class LicenseChecker +{ + private const double ClientLimitOverExceedingFactor = 1.3; + + private static readonly License FreeLicense = new() { ClientLimit = 2, IssuerLimit = 1 }; + private static readonly LicenseManager LicenseManager = new(); + + private static ConcurrentDictionary? _knownClientIds; + private static ConcurrentDictionary? _knownIssuers; + + /// + /// Registers a new license with the license management system, allowing for real-time updates + /// to the application's licensing constraints. + /// + /// The license to add to the system. + internal static void AddLicense(License license) => LicenseManager.AddLicense(license); + + /// + /// Asynchronously applies licensing checks to a task that returns client information. + /// + /// The task returning client information to be checked against licensing constraints. + /// + /// A task that, upon completion, returns the client information if it complies with the licensing + /// constraints; otherwise, logs an error. + public static async Task WithLicenseCheck(this Task clientInfo) + => (await clientInfo).CheckClient(); + + /// + /// Applies licensing checks to client information. + /// + /// The client information to check against licensing constraints. + /// The client information if it complies with the licensing constraints; otherwise, logs an error. + /// + public static ClientInfo? CheckClient(this ClientInfo? clientInfo) + { + if (clientInfo != null) + { + var utcNow = DateTimeOffset.UtcNow; + var currentLicense = LicenseManager.TryGetCurrentLicenseLimit(utcNow) ?? FreeLicense; + if (currentLicense.ClientLimit.HasValue) + { + _knownClientIds ??= new ConcurrentDictionary(StringComparer.Ordinal); + if (currentLicense.ClientLimit.Value * ClientLimitOverExceedingFactor < _knownClientIds.Count && + !_knownClientIds.ContainsKey(clientInfo.ClientId)) + { + if (LicenseLogger.Instance.IsAllowed(new { clientInfo.ClientId }, utcNow, TimeSpan.FromMinutes(1))) + { + LicenseLogger.Instance.LogCritical( + "Client limit exceeded: licensed for {ClientLimit} clients, current count exceeds by more than 30%. Used client IDs: {@ClientIds}, new client ID: {ClientId}", + currentLicense.ClientLimit, + _knownClientIds.Keys, + clientInfo.ClientId); + } + + return null; // Prevents processing of clients exceeding the limit by more than 30% + } + + _knownClientIds.TryAdd(clientInfo.ClientId, null!); + if (currentLicense.ClientLimit.Value < _knownClientIds.Count && + LicenseLogger.Instance.IsAllowed(new { clientInfo.ClientId }, utcNow, TimeSpan.FromMinutes(15))) + { + LicenseLogger.Instance.LogError( + "Licensed client limit of {ClientLimit} exceeded. Current clients: {@ClientIds}. Immediate license upgrade required", + currentLicense.ClientLimit.Value, _knownClientIds.Keys); + } + } + } + + return clientInfo; + } + + /// + /// Applies licensing checks to an issuer value. + /// + /// The issuer to check against licensing constraints. + /// The issuer if it complies with the licensing constraints; otherwise, logs an error. + public static string CheckIssuer(string issuer) + { + var utcNow = DateTimeOffset.UtcNow; + var currentLicense = LicenseManager.TryGetCurrentLicenseLimit(utcNow) ?? FreeLicense; + + if (currentLicense.ValidIssuers is { Count: > 0 } && !currentLicense.ValidIssuers.Contains(issuer)) + { + // Log error: the allowed list of issuers does not contain current value. + LicenseLogger.Instance.LogCritical("The issuer {Issuer} is not allowed by current license. The list of allowed issuers is {@Issuers}", + issuer, currentLicense.ValidIssuers); + + throw new InvalidOperationException("The license terms violation detected"); + } + + if (currentLicense.IssuerLimit.HasValue) + { + _knownIssuers ??= new ConcurrentDictionary(StringComparer.Ordinal); + _knownIssuers.TryAdd(issuer, null!); + if (currentLicense.IssuerLimit.Value < _knownIssuers.Count && + LicenseLogger.Instance.IsAllowed(new { issuer }, utcNow, TimeSpan.FromMinutes(15))) + { + // Log error: Exceeded the licensed limit of issuers. + LicenseLogger.Instance.LogError("Exceeded the licensed limit of issuers: {IssuerLimit}. The list of used issuers is {@Issuers}", + currentLicense.IssuerLimit.Value, _knownIssuers.Keys); + + throw new InvalidOperationException("The license terms violation detected"); + } + } + + return issuer; + } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseLoader.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseLoader.cs new file mode 100644 index 00000000..2653bb5c --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseLoader.cs @@ -0,0 +1,133 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Exceptions; +using Abblix.Utils; +using JsonWebKey = Abblix.Jwt.JsonWebKey; + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Handles the loading and validation of application licenses provided as JSON Web Tokens (JWT). +/// +/// +/// This class is responsible for validating the integrity and authenticity of the license JWT against predefined +/// criteria, including issuer validation and signature verification. Upon successful validation, it extracts and +/// applies license details to configure application features and limits accordingly. +/// +public class LicenseLoader +{ + private const string ValidIssuer = "https://abblix.com"; + private const string ValidLicenseType = "urn:abblix.com:oidc.server:license"; + + /// + /// Asynchronously loads and validates the license JWT, applying the license details upon successful validation. + /// + /// The license JWT string to be loaded and validated. + /// + /// Thrown if the JWT type is not valid or if the license cannot be validated. + /// Thrown if an unexpected validation result type is encountered. + /// + /// A representing the asynchronous operation. + public static async Task LoadAsync(string licenseJwt) + { + var validationResult = await new JsonWebTokenValidator().ValidateAsync( + licenseJwt, + new ValidationParameters + { + Options = ValidationOptions.ValidateIssuer | + ValidationOptions.RequireSignedTokens | + ValidationOptions.ValidateIssuerSigningKey, + + ValidateIssuer = ValidateIssuer, + ResolveIssuerSigningKeys = ResolveIssuerSigningKeys, + }); + + switch (validationResult) + { + case ValidJsonWebToken { Token.Header.Type: ValidLicenseType, Token: var token }: + var license = new License + { + NotBefore = token.Payload.NotBefore, + ExpiresAt = token.Payload.ExpiresAt, + GracePeriod = token.Payload.Json.GetUnixTimeSeconds("grace_period"), + ClientLimit = token.Payload["client_limit"]?.GetValue(), + IssuerLimit = token.Payload["issuer_limit"]?.GetValue(), + ValidIssuers = token.Payload.Json.GetArrayOfStrings("valid_issuers").ToHashSet(StringComparer.Ordinal), + }; + LicenseChecker.AddLicense(license); + break; + + case ValidJsonWebToken: + throw new InvalidOperationException("The JWT type is not valid"); + + case JwtValidationError { Error: var error, ErrorDescription: var description}: + throw new InvalidOperationException( + $"The license can't be validated: [{error}] {description}"); + + default: + throw new UnexpectedTypeException(nameof(validationResult), validationResult.GetType()); + } + } + + /// + /// Validates the issuer of the license JWT against a predefined valid issuer. + /// + /// The issuer URL to validate. + /// A indicating whether the issuer is valid. + private static Task ValidateIssuer(string issuer) + => Task.FromResult(issuer == ValidIssuer); + + /// + /// Resolves the signing keys for the issuer of the license JWT, required for signature validation. + /// + /// The issuer URL whose signing keys are to be resolved. + /// An asynchronous stream of objects representing the issuer's signing keys. + /// + private static async IAsyncEnumerable ResolveIssuerSigningKeys(string issuer) + { + if (issuer != ValidIssuer) + yield break; + + var pem = await GetSigningKeyPem(); + var certificate = X509Certificate2.CreateFromPem(pem); + var jwk = certificate.ToJsonWebKey(); + yield return jwk; + } + + /// + /// Retrieves the PEM-encoded signing key for the license JWT from embedded resources. + /// + /// A representing the PEM-encoded signing key. + private static async Task GetSigningKeyPem() + { + var type = typeof(LicenseLoader); + var name = $"{type.Namespace}.Resources.Abblix Licensing.pem"; + + await using var stream = type.Assembly.GetManifestResourceStream(name).NotNull(name); + using var reader = new StreamReader(stream, Encoding.UTF8); + return await reader.ReadToEndAsync(); + } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseLoadingService.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseLoadingService.cs new file mode 100644 index 00000000..a0c58c21 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseLoadingService.cs @@ -0,0 +1,94 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// A hosted service that loads the license JWT at application startup. +/// +/// +/// This service is responsible for retrieving the license JWT using the provided +/// and loading it into the application's licensing system. It ensures that the application operates with the correct +/// licensing configuration from the outset, supporting features and limitations as defined by the license. +/// +/// The service runs as part of the application's background services, ensuring the license is loaded before +/// the application starts accepting incoming requests. +/// +internal class LicenseLoadingService + : IHostedService +{ + /// + /// Initializes a new instance of the with the specified license JWT provider. + /// + /// + /// The provider used to retrieve the license JWT. + public LicenseLoadingService( + ILoggerFactory loggerFactory, + ILicenseJwtProvider licenseJwtProvider) + { + LicenseLogger.Instance.Init(loggerFactory); + _licenseJwtProvider = licenseJwtProvider; + } + + private readonly ILicenseJwtProvider _licenseJwtProvider; + + /// + /// Starts the service by loading the license JWT. + /// + /// A used to observe + /// when the startup process is aborted. + /// A representing the asynchronous operation of loading the license JWT. + /// + /// If a valid license JWT is retrieved from the , it is loaded to configure + /// the application's licensing system. This method is called automatically by the .NET hosting environment + /// when the application starts. + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + var licenses = _licenseJwtProvider.GetLicenseJwtAsync(); + if (licenses != null) + { + await foreach (var license in licenses) + { + if (license.HasValue()) + await LicenseLoader.LoadAsync(license); + } + } + } + + /// + /// Stops the service. + /// + /// A used to observe + /// when the shutdown process is aborted. + /// A that represents the completion of the service's stop operation. + /// + /// This method is called automatically by the .NET hosting environment when the application is shutting down. + /// Since this service does not maintain any resources that need to be explicitly released on stop, the method + /// completes immediately. + /// + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseLogger.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseLogger.cs new file mode 100644 index 00000000..ab38ceee --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseLogger.cs @@ -0,0 +1,126 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// A specialized logger for licensing checks, extending the standard logging functionality to include +/// throttling capabilities for repetitive log messages. +/// +internal class LicenseLogger: ILogger +{ + private LicenseLogger() + { + var dictionary = new ConcurrentDictionary(); + + _timer = new Timer(state => + { + var dict = (ConcurrentDictionary)state!; + var utcNow = DateTimeOffset.UtcNow; + foreach (var item in dict) + { + if (item.Value < utcNow) + dict.TryRemove(item.Key, out _); + } + }, + dictionary, + TimeSpan.FromMinutes(1), + TimeSpan.FromMinutes(1)); + + _nextAllowedTimes = dictionary; + } + + public static LicenseLogger Instance { get; } = new(); + + /// + /// Logs a message if the specified conditions are met, implementing throttling to prevent excessive logging + /// of similar messages. + /// + /// The type of the object to log. + /// The severity level of the log message. + /// The event ID of the log message. + /// The state related to the log message. + /// The exception related to the log message, if any. + /// A function to create a string message from the state and exception. + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + => _logger.Log(logLevel, eventId, state, exception, formatter); + + /// + /// Checks if logging at the specified log level is enabled. + /// + /// The log level to check. + /// True if logging is enabled at the specified log level; otherwise, false. + public bool IsEnabled(LogLevel logLevel) + => _logger.IsEnabled(logLevel); + + /// + /// Begins a logical operation scope. + /// + /// The type of the state for the scope. + /// The state for the new scope. + /// An IDisposable representing the scope. + public IDisposable? BeginScope(TState state) where TState : notnull + => _logger.BeginScope(state); + + private const string LoggerName = "Abblix.Oidc.Server"; + private ILogger _logger = NullLogger.Instance; + private readonly ConcurrentDictionary _nextAllowedTimes; + private Timer _timer; + + /// + /// Initializes the logger with a specific logger factory. + /// + /// The factory used to create the underlying logger instance. + internal void Init(ILoggerFactory loggerFactory) => _logger = loggerFactory.CreateLogger(LoggerName); + + /// + /// Determines whether a log write operation is allowed based on the specified key and period. + /// + /// The key identifying the log operation, used to prevent repetitive logging of similar messages. + /// The current UTC time, used to calculate the time elapsed since the last log write. + /// The period within which repetitive log messages are throttled. + /// True if the log write operation is allowed; otherwise, false. + /// + /// This method helps to throttle logging by allowing log messages to be written only if a specified period has elapsed + /// since the last log write for a given key. This prevents flooding the log with repetitive messages. + /// + public bool IsAllowed(object key, DateTimeOffset utcNow, TimeSpan period) + { + var newTime = utcNow + period; + if (_nextAllowedTimes.TryAdd(key, newTime)) + { + return true; + } + + if (_nextAllowedTimes.TryGetValue(key, out var nextAllowedTime) && nextAllowedTime < utcNow && + _nextAllowedTimes.TryUpdate(key, newTime, nextAllowedTime)) + { + return true; + } + + return false; + } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseManager.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseManager.cs new file mode 100644 index 00000000..73c1056c --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseManager.cs @@ -0,0 +1,293 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Logging; + +[assembly:InternalsVisibleTo("Abblix.Oidc.Server.UnitTests")] + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Manages the application's licenses, ensuring that the current license is appropriately evaluated based +/// on its validity period. +/// +/// +/// This class supports the addition of multiple licenses and determines the active license by considering their +/// validity periods. It uses a thread-safe approach to manage concurrent access to the licenses list, +/// allowing for efficient reads and safe updates. +/// +public class LicenseManager +{ + private volatile License? _currentLicense; + private readonly List _licenses = new(); + private readonly ReaderWriterLockSlim _rwLock = new(); + private int _currentLicenseIndex; + + /// + /// Adds a new license to the application, placing it in the correct position based on its validity period. + /// + /// The license to be added. + /// + /// The method inserts the license into a sorted list, ensuring that licenses are ordered based on their + /// validity periods. This ordering facilitates the determination of the current active license. + /// + public void AddLicense(License license) + { + _rwLock.EnterWriteLock(); + try + { + var i = _licenses.BinarySearch(license, new ActivityPeriodComparer()); + _licenses.Insert(i < 0 ? ~i : i, license); + + _currentLicense = GenerateActiveLicense(DateTimeOffset.UtcNow); + } + finally + { + _rwLock.ExitWriteLock(); + } + } + + /// + /// Provides a mechanism to compare two licenses based on their activity periods, facilitating sorting. + /// + private sealed class ActivityPeriodComparer : IComparer + { + /// + /// Compares two licenses based on their NotBefore and ExpiresAt values. + /// + /// The first license to compare. + /// The second license to compare. + /// An integer indicating the relative order of the licenses. + public int Compare(License? x, License? y) + { + var notBeforeComparison = Compare(x?.NotBefore, y?.NotBefore, DateTimeOffset.MinValue); + if (notBeforeComparison != 0) + return notBeforeComparison; + + return Compare(x?.ExpiresAt, y?.ExpiresAt, DateTimeOffset.MaxValue); + } + + private static int Compare(DateTimeOffset? x, DateTimeOffset? y, DateTimeOffset defaultValue) + => x.GetValueOrDefault(defaultValue).CompareTo(y.GetValueOrDefault(defaultValue)); + } + + /// + /// Attempts to retrieve the current license from the LicenseManager based on the given moment in time. + /// + /// The current UTC time to determine the active license. + /// The current license if one is active and valid, otherwise null. + public License? TryGetCurrentLicenseLimit(DateTimeOffset utcNow) + { + static bool IsExpired(License? license, DateTimeOffset utcNow) => license is null || license.ExpiresAt < utcNow; + + var currentLicense = _currentLicense; + if (!IsExpired(currentLicense, utcNow)) + return currentLicense; + + _rwLock.EnterReadLock(); + try + { + while (IsExpired(currentLicense, utcNow)) + { + var newLicense = GenerateActiveLicense(utcNow); + if (Interlocked.CompareExchange(ref _currentLicense, newLicense, currentLicense) == currentLicense) + { + return newLicense; + } + } + + return _currentLicense; + } + finally + { + _rwLock.ExitReadLock(); + } + } + + /// + /// Generates the active license based on the current UTC time, taking into account licenses that are about to expire, + /// currently active, or within their grace period. + /// + /// The current UTC time used for evaluating the active license. + /// A license that is determined to be active based on the current time, or null if no such license exists. + /// + /// This method evaluates all licenses managed by the LicenseManager, considering their validity periods and grace periods, + /// to determine which license is currently active. It supports dynamic updates to the active license as time progresses + /// and as licenses expire or become active. + /// + internal License? GenerateActiveLicense(DateTimeOffset utcNow) + { + License? result = null; + bool? activeLicenseFound = null; + for (var indexCurrent = _currentLicenseIndex; indexCurrent < _licenses.Count; indexCurrent++) + { + var license = _licenses[indexCurrent]; + var status = GetLicenseStatus(license, utcNow); + switch (status) + { + case LicenseStatus.Expired: + Interlocked.Increment(ref _currentLicenseIndex); + break; + + case LicenseStatus.Active: + result = AppendLicense(result, license, status, utcNow); + break; + + case LicenseStatus.GracePeriod: + activeLicenseFound ??= FindActiveLicensesInFuture(utcNow, ref indexCurrent, ref result); + + if (activeLicenseFound == false) + result = AppendLicense(result, license, status, utcNow); + + break; + + case LicenseStatus.NotActiveYet: + return result; + } + } + + return result; + } + + /// + /// Searches for active licenses that will become valid in the future, starting from the current index in the licenses list. + /// + /// The current UTC time for license evaluation. + /// The current index in the licenses list from which to start the search. + /// The license that has been determined to be active or will soon be active, to be updated by this method. + /// True if an active license is found in the future; otherwise, false. + /// + /// This method is used internally by GenerateActiveLicense to find licenses that are not yet active but will become so, + /// allowing for a seamless transition between licenses as they expire or become valid. + /// + private bool FindActiveLicensesInFuture(DateTimeOffset utcNow, ref int indexCurrent, ref License? result) + { + for (var indexNext = indexCurrent + 1; indexNext < _licenses.Count; indexNext++) + { + var nextLicense = _licenses[indexNext]; + var nextStatus = GetLicenseStatus(nextLicense, utcNow); + if (nextStatus == LicenseStatus.GracePeriod) + continue; + + indexCurrent = indexNext; + Interlocked.Exchange(ref _currentLicenseIndex, indexNext); + + result = AppendLicense(result, nextLicense, nextStatus, utcNow); + return true; + } + + return false; + } + + + /// + /// Appends a given license to the result, potentially updating the result based on the status of the given license. + /// + /// The current result license, which may be updated by this method. + /// The license to append or compare against the result. + /// The status of the given license. + /// The current UTC time for evaluating the license's status. + /// The updated result license after considering the given license. + /// + /// Depending on the status of the given license, this method may log warnings or errors about license expiration + /// and updates the result license to reflect the most appropriate active license based on the current time. + /// + private static License AppendLicense(License? result, License license, LicenseStatus status, DateTimeOffset utcNow) + { + switch (status) + { + case LicenseStatus.Active + when license is { ExpiresAt: {} expiresAt } && expiresAt < utcNow.AddMonths(1) && + LicenseLogger.Instance.IsAllowed(new { license, status }, utcNow, TimeSpan.FromDays(1)): + + LicenseLogger.Instance.LogWarning( + "License expiring soon: {ExpiresAt:R}. Please renew promptly to avoid service interruption", + expiresAt); + break; + + case LicenseStatus.GracePeriod + when license is { ExpiresAt: {} expiresAt } && + LicenseLogger.Instance.IsAllowed(new { license, status }, utcNow, TimeSpan.FromDays(1)): + + LicenseLogger.Instance.LogError( + "License expired on {ExpiresAt:R}. Renew immediately to maintain service access", + expiresAt); + break; + + case LicenseStatus.Expired + when license is { ExpiresAt: {} expiresAt } && + LicenseLogger.Instance.IsAllowed(new { license, status }, utcNow, TimeSpan.FromDays(1)): + + LicenseLogger.Instance.LogCritical( + "License expired on {ExpiresAt:R}, {ExpiredDaysAgo} days ago. Service access will be affected. Renewal is required as soon as possible!", + expiresAt, + (int)(utcNow - expiresAt).TotalDays); + break; + } + + if (result == null) + { + result = license; + } + else + { + result = result with { + ClientLimit = result.ClientLimit.Greater(license.ClientLimit), + IssuerLimit = result.IssuerLimit.Greater(license.IssuerLimit), + ExpiresAt = result.ExpiresAt.Lesser(license.ExpiresAt), + ValidIssuers = result.ValidIssuers.Join(result.ValidIssuers), + }; + } + + return result; + } + + /// + /// Determines the status of a given license at a specific moment in time. + /// + /// The license to evaluate. + /// The moment in time at which to evaluate the license. + /// The status of the license at the given moment. + private static LicenseStatus GetLicenseStatus(License license, DateTimeOffset moment) + { + return license switch + { + { NotBefore: { } notBefore } when moment < notBefore => LicenseStatus.NotActiveYet, + + { ExpiresAt: { } expiresAt, GracePeriod: { } gracePeriod } + when expiresAt < moment && moment <= gracePeriod + => LicenseStatus.GracePeriod, + + { ExpiresAt: { } expiresAt } when expiresAt < moment + => LicenseStatus.Expired, + + _ => LicenseStatus.Active, + }; + } + + /// + /// Provides access to the currently managed licenses. + /// + /// A sequence of all licenses managed by the LicenseManager. + public IEnumerable GetLicenses() => _licenses; +} diff --git a/Abblix.Oidc.Server/Features/Licensing/LicenseStatus.cs b/Abblix.Oidc.Server/Features/Licensing/LicenseStatus.cs new file mode 100644 index 00000000..09041664 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/LicenseStatus.cs @@ -0,0 +1,51 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// Specifies the status of a license in relation to its validity period and current date and time. +/// +public enum LicenseStatus +{ + /// + /// Indicates that the license is not active yet according to its defined validity period. + /// + NotActiveYet, + + /// + /// Indicates that the license is currently active and within its validity period. + /// + Active, + + /// + /// Indicates that the license has expired but is still within its grace period, + /// during which it may continue to be considered as valid under certain conditions. + /// + GracePeriod, + + /// + /// Indicates that the license has expired and is beyond its grace period, if any, + /// and is therefore no longer valid. + /// + Expired, +} diff --git a/Abblix.Oidc.Server/Features/Licensing/OptionsLicenseJwtProvider.cs b/Abblix.Oidc.Server/Features/Licensing/OptionsLicenseJwtProvider.cs new file mode 100644 index 00000000..0152bb41 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/OptionsLicenseJwtProvider.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Utils; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.Licensing; + +public class OptionsLicenseJwtProvider : ILicenseJwtProvider +{ + /// + /// Initializes a new instance of the class. + /// + /// The OIDC options containing the license JWT. + public OptionsLicenseJwtProvider(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Asynchronously retrieves the license JWT from the OIDC service configuration. + /// + /// A task representing the asynchronous operation, which upon completion contains the license JWT used for + /// validating the configuration and licensing terms of the OIDC service. + public IAsyncEnumerable? GetLicenseJwtAsync() + { + var licenseJwt = _options.Value.LicenseJwt; + return licenseJwt != null ? new[] { licenseJwt }.AsAsync() : null; + } +} diff --git a/Abblix.Oidc.Server/Features/Licensing/Resources/Abblix Licensing.pem b/Abblix.Oidc.Server/Features/Licensing/Resources/Abblix Licensing.pem new file mode 100644 index 00000000..0919633e --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/Resources/Abblix Licensing.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF6TCCA9GgAwIBAgIUTFyJl6+PV2+LkdduyQMkE+YSEI4wDQYJKoZIhvcNAQEN +BQAwgakxCzAJBgNVBAYTAktaMQ8wDQYDVQQIDAZBc3RhbmExDzANBgNVBAcMBkFz +dGFuYTEPMA0GA1UECgwGQWJibGl4MRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MSww +KgYDVQQDDCNBYmJsaXggUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8G +CSqGSIb3DQEJARYSc3VwcG9ydEBhYmJsaXguY29tMB4XDTI0MDIxOTA5NTYwMloX +DTM0MDIxNjA5NTYwMlowYjELMAkGA1UEBhMCS1oxDzANBgNVBAgMBkFzdGFuYTEP +MA0GA1UECgwGQWJibGl4MRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MRkwFwYDVQQD +DBBBYmJsaXggTGljZW5zaW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEApOJ5eZ5JLq+ppTDYdAwrg7gLjM7zoiLv5i0qfIe8jkrrYYX8DdO/A5DiKlbN +RgqQIoNkmQCqwD6XQ8n0qDt05gku9ByN/GDYgN7dyvc6Hz0cOOuURhtOiewccrYI +FIY+jfLEmJ4NpwlOGnwbrNaETlCMm0dL0pkwxPOxlmPI7j2Wdz0zzMaBPfUKB8ix +/SNo/EUZob3DYAFCFvqx7H2XXlzKIrUhniQQRv1iyEAEtuC8J/axpudMox6DidAl +FUcQkXv1P11kBgZItn45kxdMQy5ye2EGEeFM3g8fkaKnQwInriAi+k6KjkgIzS+s +4gEYK+GhNJNJG2PC/e39UjRCIs0V5FKZAx7f22VRXaXZdotHLZVQzMjJdV1RuU60 +LB5naEMZMjDA35+peNAzyv6QCdoCRnTPafRzytGRyBdvjoyw4m/Pix0ht0nyzrn2 +fJhFwtrqJhu73sEWRfN/kR8Hse5RRz7IqC11XaLwPUi9RYCjX6Gpcuf2v2t2uEzm +cXQE+WRFpR/Sx6ssT4dQpOzJ4gn5YZ4A9b2ehtmoxzd3ySc9lZtOCE0j4EYn//qC +Zx5bqOCQ4aVJFsjMP6HqaGzsuKbpGi1TBlI/Vwoogkn5KWHE3DgdNzykni3ClQD5 +GEH3wRiJAA/MoEm1KJioV8mpNW5yVvjoJOft708rmwhxMjsCAwEAAaNPME0wCwYD +VR0PBAQDAgeAMB0GA1UdDgQWBBQOwMCu5YhIbxMVvt0GFubj2A2kBzAfBgNVHSME +GDAWgBSHtw6S4MYmee0FFYnFe/W8gsiLqDANBgkqhkiG9w0BAQ0FAAOCAgEAKjEI +baNKZuCb2C8Vadj8AND1nzqUrdVEd4RsLf0AQVzwcIrrykzM+FszbyqQ4LvkEIN5 +jByF+771tYIdehFNO8mC+Q+mtDDx1KI4A5T9AzfJ94YIdahrSXNnivSMh5L+5AmE +8CyF8atgeCrsJ/8fzMBqJVz0gcRtF5tzVxsmc6X7TNWfJpg7xNX6QtKM4KW9Hoa4 +WygmSP8NbrAQkacl5/wcW0pRjAvPxcqCLxvWkQ6Wsv/smDCnUqcCt7Yoewtu+WUy +p8x/lre9L3QnWhadYSisEKYpWzKyPn/xJY0zQjdjrZ4gJb+9vjxclLcPBIFSt8k+ +8Rm51NPTS7smlatRD/6AJV5fJxPnskHSQr527cM93mRK2ExzEJ8TQ6Y6UBXQ3HRW +xUL+Tmqb7VvTlsbgU1gE1VhQ8pr61mUQxLVxEKOGQwC/4RxR+hl8IQiPEDjML0Gz +KmhxOCtHrjWA7+IUIerVlexjVmHdVHuMUI07cRab1c7qYOW2uO+ddt00VjTf1x1l +bAUOvRWcfn/2WWGkqj4w6jsXjick9eh091WWstrRCd0vj0GrzmH7jjUcd/82K0I9 +Vw6WlRDbc4Gj8jzCU3xM1W7QTL4ynJZLVi5Woi7lWkqzqHPT2SGXntp6FYPg95aC +PoyvqmIb7JbFmMlYH0SCAbfn/QGC3tciX7AzXAk= +-----END CERTIFICATE----- diff --git a/Abblix.Oidc.Server/Features/Licensing/StaticLicenseJwtProvider.cs b/Abblix.Oidc.Server/Features/Licensing/StaticLicenseJwtProvider.cs new file mode 100644 index 00000000..3f2311b7 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Licensing/StaticLicenseJwtProvider.cs @@ -0,0 +1,58 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.Licensing; + +/// +/// An implementation of that returns a predefined license JWT string. +/// +/// +/// This class is designed for scenarios where the license JWT is statically known at the time of application +/// initialization. It could be particularly useful in testing environments or situations where the license JWT +/// is obtained from external sources and passed directly to the application without the need for asynchronous +/// retrieval from a configuration store or service. +/// +public class StaticLicenseJwtProvider : ILicenseJwtProvider +{ + /// + /// Initializes a new instance of the class with a specified license JWT. + /// + /// The license JWT to be used for OIDC service configuration validation. + /// Thrown if is null or empty. + public StaticLicenseJwtProvider(string licenseJwt) + { + _licenseJwt = licenseJwt; + } + + private readonly string _licenseJwt; + + /// + /// Asynchronously returns the predefined license JWT string. + /// + /// A task that represents the asynchronous operation, resulting in the license JWT string. + public IAsyncEnumerable? GetLicenseJwtAsync() + { + return new[] { _licenseJwt }.AsAsync(); + } +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutNotifier.cs b/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutNotifier.cs new file mode 100644 index 00000000..585a942d --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutNotifier.cs @@ -0,0 +1,84 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Tokens; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Implements the mechanism for notifying clients about logout events through the back-channel, +/// leveraging logout tokens to securely communicate the logout state to client applications. +/// +public class BackChannelLogoutNotifier : ILogoutNotifier +{ + /// + /// Initializes a new instance of the class, setting up + /// the services for logout token creation and distribution. + /// + /// The service responsible for creating logout tokens that encapsulate + /// the details of the logout event. + /// The service responsible for sending the logout tokens to the client + /// applications via back-channel communication. + public BackChannelLogoutNotifier( + ILogoutTokenService logoutTokenService, + ILogoutTokenSender logoutTokenSender) + { + _logoutTokenService = logoutTokenService; + _logoutTokenSender = logoutTokenSender; + } + + private readonly ILogoutTokenService _logoutTokenService; + private readonly ILogoutTokenSender _logoutTokenSender; + + /// + /// Asynchronously notifies a client of a logout event by creating a logout token and sending it to the client's + /// back-channel logout endpoint. This ensures that the client application is informed about the logout event + /// and can take appropriate actions, such as invalidating the user's session. + /// + /// The client information, including the back-channel logout URI, to which the logout + /// notification should be sent. + /// The context of the logout event, containing details such as the subject identifier + /// and session identifier, which are included in the logout token. + /// A task that represents the asynchronous operation of notifying the client. The task completes when + /// the notification has been successfully sent to the client's back-channel logout endpoint. + public async Task NotifyClientAsync(ClientInfo clientInfo, LogoutContext logoutContext) + { + // Ensure the client supports back-channel logout + if (clientInfo.BackChannelLogout == null) + return; + + // Create the logout token specific to the logout event and client + var logoutToken = await _logoutTokenService.CreateLogoutTokenAsync(clientInfo, logoutContext); + + // Send the logout token to the client's back-channel logout endpoint + await _logoutTokenSender.SendBackChannelLogoutAsync(clientInfo, logoutToken); + } + + public bool FrontChannelLogoutSupported => false; + + public bool FrontChannelLogoutSessionSupported => false; + + public bool BackChannelLogoutSupported => true; + + public bool BackChannelLogoutSessionSupported => true; +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutTokenSender.cs b/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutTokenSender.cs new file mode 100644 index 00000000..00d58bbd --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/BackChannelLogoutTokenSender.cs @@ -0,0 +1,92 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Implements the functionality to send logout tokens to clients via back-channel communication, +/// adhering to the OpenID Connect back-channel logout specification. +/// +public class BackChannelLogoutTokenSender : ILogoutTokenSender +{ + /// + /// Initializes a new instance of the class with the specified logger, + /// JWT formatter, and HTTP client. + /// + /// The logger to use for logging information about the logout token sending process. + /// The formatter responsible for creating JWTs to be sent as logout tokens. + /// The HTTP client used for sending the logout tokens to clients over the + /// back channel. + public BackChannelLogoutTokenSender( + ILogger logger, + IClientJwtFormatter jwtFormatter, + HttpClient backChannelHttpClient) + { + _logger = logger; + _jwtFormatter = jwtFormatter; + _backChannelHttpClient = backChannelHttpClient; + } + + private readonly ILogger _logger; + private readonly IClientJwtFormatter _jwtFormatter; + private readonly HttpClient _backChannelHttpClient; + + /// + /// Asynchronously sends a logout token directly to a client over the back channel. + /// + /// Information about the client to which the logout token is sent. + /// The logout token to be sent. + /// A task representing the asynchronous operation of sending the logout token. + /// + /// This method constructs a back-channel HTTP POST request containing the logout token + /// and sends it to the client's back-channel logout URI. + /// It ensures that the HTTP response indicates successful delivery of the logout token. + /// + public async Task SendBackChannelLogoutAsync(ClientInfo clientInfo, EncodedJsonWebToken logoutToken) + { + var logoutOptions = clientInfo.BackChannelLogout.NotNull(nameof(clientInfo.BackChannelLogout)); + + var parameters = new KeyValuePair[] + { + new ("logout_token", logoutToken.EncodedJwt), + }; + + using var content = new FormUrlEncodedContent(parameters); + using var response = await _backChannelHttpClient.PostAsync(logoutOptions.Uri, content); + + _logger.LogDebug("The request with {@Parameters} was sent to {Uri}, the status code {StatusCode} was received", + parameters, logoutOptions.Uri, response.StatusCode); + + response.EnsureSuccessStatusCode(); + if (!response.IsSuccessStatusCode) + { + _logger.LogError("Failed to send logout token to {Uri}. Status code: {StatusCode}", + logoutOptions.Uri, response.StatusCode); + } + } +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/CompositeLogoutNotifier.cs b/Abblix.Oidc.Server/Features/LogoutNotification/CompositeLogoutNotifier.cs new file mode 100644 index 00000000..e1abb58a --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/CompositeLogoutNotifier.cs @@ -0,0 +1,74 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Provides a mechanism to aggregate and execute multiple logout notification strategies for an OpenID Connect or OAuth 2.0 system. +/// +/// +/// This class allows the system to support various logout mechanisms simultaneously, such as front-channel and back-channel logout, +/// by combining multiple implementations. It ensures that all configured logout notifiers are invoked +/// to notify clients about the logout event, catering to different client capabilities and configurations. +/// +public class CompositeLogoutNotifier: ILogoutNotifier +{ + /// + /// Initializes a new instance of the class. + /// + /// An array of implementations for handling logout notifications. + public CompositeLogoutNotifier(ILogoutNotifier[] logoutNotifiers) + { + _logoutNotifiers = logoutNotifiers; + } + + private readonly ILogoutNotifier[] _logoutNotifiers; + + /// + /// Asynchronously notifies all configured clients about a logout event by invoking each registered logout notifier. + /// + /// The information about the client that is being notified of the logout event. + /// Contextual information related to the logout event, including the user and session identifiers. + /// A task that represents the asynchronous operation of notifying all clients. + /// + /// This method ensures that each logout notifier is called, regardless of the individual notifier's outcome. + /// It allows for a unified approach to logout notifications, accommodating various client requirements and logout mechanisms. + /// + public Task NotifyClientAsync(ClientInfo clientInfo, LogoutContext logoutContext) + { + var tasks = Array.ConvertAll( + _logoutNotifiers, + notifier => notifier.NotifyClientAsync(clientInfo, logoutContext)); + + return Task.WhenAll(tasks); + } + + public bool FrontChannelLogoutSupported => _logoutNotifiers.Any(notifier => notifier.FrontChannelLogoutSupported); + + public bool FrontChannelLogoutSessionSupported => _logoutNotifiers.Any(notifier => notifier.FrontChannelLogoutSessionSupported); + + public bool BackChannelLogoutSupported => _logoutNotifiers.Any(notifier => notifier.BackChannelLogoutSupported); + + public bool BackChannelLogoutSessionSupported => _logoutNotifiers.Any(notifier => notifier.BackChannelLogoutSessionSupported); +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/FrontChannelLogoutNotifier.cs b/Abblix.Oidc.Server/Features/LogoutNotification/FrontChannelLogoutNotifier.cs new file mode 100644 index 00000000..655ee853 --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/FrontChannelLogoutNotifier.cs @@ -0,0 +1,94 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Implements the mechanism for notifying clients of logout events through the front-channel, +/// typically by redirecting the user's browser to a client-specific logout URL. +/// +public class FrontChannelLogoutNotifier: ILogoutNotifier +{ + /// + /// Asynchronously notifies a client about a logout event by constructing and storing + /// the front-channel logout URI, which can be used to redirect the user's browser + /// to perform the logout process on the client's side. + /// + /// Information about the client that needs to be notified of the logout event. + /// Contextual information about the logout event, including the session ID and issuer. + /// A task that represents the asynchronous notification operation. + /// + /// This method constructs a logout URI based on the client's configuration for front-channel logout, + /// including necessary parameters such as issuer and session ID if by the client. + /// It then adds the constructed URI to a collection for later use, typically for redirection. + /// + public Task NotifyClientAsync(ClientInfo clientInfo, LogoutContext logoutContext) + { + NotifyClient(clientInfo, logoutContext); + return Task.CompletedTask; + } + + public bool FrontChannelLogoutSupported => true; + + public bool FrontChannelLogoutSessionSupported => true; + + public bool BackChannelLogoutSupported => false; + + public bool BackChannelLogoutSessionSupported => false; + + private static void NotifyClient(ClientInfo clientInfo, LogoutContext logoutContext) + { + if (clientInfo is not { FrontChannelLogout: { Uri: var uri, RequiresSessionId: var requiresSid } }) + return; + + if (requiresSid) + { + if (string.IsNullOrEmpty(logoutContext.SessionId)) + { + throw new InvalidOperationException($"The client {clientInfo.ClientId} requires session id"); + } + + uri = new UriBuilder(uri) + { + Query = + { + [Parameters.Issuer] = logoutContext.Issuer, + [Parameters.SessionId] = logoutContext.SessionId, + } + }; + } + + // Store the logout URI for later redirection + logoutContext.FrontChannelLogoutRequestUris.Add(uri); + } + + /// + /// Contains constants for the query parameter names used in constructing front-channel logout URIs. + /// + private static class Parameters + { + public const string Issuer = "iss"; + public const string SessionId = "sid"; + } +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutNotifier.cs b/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutNotifier.cs new file mode 100644 index 00000000..0412798e --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutNotifier.cs @@ -0,0 +1,78 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Defines an interface for a service responsible for notifying clients about logout events. +/// This interface supports both front-channel and back-channel logout mechanisms, allowing +/// implementations to handle client notifications through direct user agent redirection or +/// server-to-server communication, respectively. +/// +public interface ILogoutNotifier +{ + /// + /// Asynchronously notifies a client about a logout event, providing the client with + /// information necessary to process the logout on its end. + /// + /// The information about the client that needs to be notified. + /// This includes details such as the client ID and the logout endpoint URI. + /// The context of the logout event, including any relevant + /// information such as the session ID and the subject ID of the user. This context + /// is essential for clients to understand the scope and reason for the logout, enabling + /// them to perform appropriate actions, such as clearing session data or redirecting the user. + /// A representing the asynchronous operation of notifying the client. + /// The task completes when the notification has been successfully sent to the client or + /// an attempt has been made to notify the client. + /// + /// Implementations of this interface should handle any exceptions that occur during + /// the notification process and ensure that all clients are notified as configured, + /// regardless of the mechanism used (front-channel or back-channel). + /// + Task NotifyClientAsync(ClientInfo clientInfo, LogoutContext logoutContext); + + /// + /// Indicates whether the logout notifier supports front-channel logout, + /// enabling clients to be notified of logout events via user-agent redirection. + /// + bool FrontChannelLogoutSupported { get; } + + /// + /// Indicates whether the logout notifier supports front-channel logout session management, + /// allowing for more precise control over session termination during a front-channel logout. + /// + bool FrontChannelLogoutSessionSupported { get; } + + /// + /// Indicates whether the logout notifier supports back-channel logout, + /// enabling server-to-server communication to notify clients of logout events. + /// + bool BackChannelLogoutSupported { get; } + + /// + /// Indicates whether the logout notifier supports back-channel logout session management, + /// facilitating the management of user sessions during a back-channel logout. + /// + bool BackChannelLogoutSessionSupported { get; } +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutTokenSender.cs b/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutTokenSender.cs new file mode 100644 index 00000000..b2fd9e01 --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/ILogoutTokenSender.cs @@ -0,0 +1,56 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Tokens; + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Defines the interface for a service responsible for sending logout tokens to clients via the back-channel. +/// +public interface ILogoutTokenSender +{ + /// + /// Asynchronously sends a logout token to a client using back-channel communication. + /// + /// The information about the client to which the logout token will be sent. + /// This includes the client's identifier and any relevant endpoints for back-channel communication. + /// The logout token that encapsulates the logout information. + /// This token is typically a JSON Web Token (JWT) that contains claims relevant to the logout event, + /// such as the subject identifier and the session identifier. + /// A representing the asynchronous operation of sending the logout token. + /// The task completes when the logout token has been successfully sent to the client's back-channel endpoint, + /// or an attempt has been made to send the token. + /// + /// Implementations of this interface are responsible for securely transmitting the logout token + /// to the client's back-channel endpoint specified in the . This process + /// usually involves making an HTTP POST request to the client's back-channel logout URI with the logout token + /// included in the request body. + /// + /// It's important for implementations to handle any errors or exceptions that may occur during + /// the transmission process and ensure proper logging and error handling mechanisms are in place. + /// This ensures that logout events are reliably communicated to clients, even in scenarios where + /// direct user-agent-based communication (front-channel logout) is not feasible. + /// + Task SendBackChannelLogoutAsync(ClientInfo clientInfo, EncodedJsonWebToken logoutToken); +} diff --git a/Abblix.Oidc.Server/Features/LogoutNotification/LogoutContext.cs b/Abblix.Oidc.Server/Features/LogoutNotification/LogoutContext.cs new file mode 100644 index 00000000..89a1dcb1 --- /dev/null +++ b/Abblix.Oidc.Server/Features/LogoutNotification/LogoutContext.cs @@ -0,0 +1,58 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.LogoutNotification; + +/// +/// Represents the context for a logout operation, containing details necessary for processing the logout. +/// This context includes the session identifier, the subject identifier of the user, the issuer of the +/// authentication token, and a collection of URIs for front-channel logout notifications. +/// +public record LogoutContext(string SessionId, string SubjectId, string Issuer) +{ + /// + /// The session ID associated with the logout event. + /// This identifier is typically used to identify and terminate the specific session that the logout request pertains to. + /// + public string SessionId { get; init; } = SessionId; + + /// + /// The subject ID of the user initiating the logout. + /// This identifier usually corresponds to the unique identifier of the user within the identity system, + /// facilitating the identification of the user across different services or components. + /// + public string SubjectId { get; init; } = SubjectId; + + /// + /// The issuer of the logout event. + /// This is typically represented by the URL of the authentication server that issued the original authentication token, + /// allowing the identification of the authority responsible for the user's authentication. + /// + public string Issuer { get; init; } = Issuer; + + /// + /// A list of URIs for sending front-channel logout requests. + /// These URIs are intended for notifying relevant parties of the logout event through the front-channel, + /// enabling the propagation of logout notifications to clients or services that need to respond to the logout event. + /// + public IList FrontChannelLogoutRequestUris { get; init; } = new List(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationCodeGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationCodeGenerator.cs new file mode 100644 index 00000000..1b9655af --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationCodeGenerator.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Utils; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Generates secure authorization codes for OAuth 2.0 authorization code flows. +/// This implementation relies on cryptographic randomness to generate codes that are difficult to predict, +/// enhancing the security of the authorization process. +/// +public class AuthorizationCodeGenerator : IAuthorizationCodeGenerator +{ + /// + /// Initializes a new instance of the class. + /// + /// Configuration options that determine the behavior of the code generation, + /// including the length of the authorization codes generated. + public AuthorizationCodeGenerator(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Generates a unique authorization code using secure cryptographic methods. The code is URL-safe encoded + /// to ensure it can be transmitted safely in URLs. + /// + /// A URL-safe, secure, and randomly generated authorization code. + public string GenerateAuthorizationCode() + => HttpServerUtility.UrlTokenEncode(CryptoRandom.GetRandomBytes(_options.Value.AuthorizationCodeLength)); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationRequestUriGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationRequestUriGenerator.cs new file mode 100644 index 00000000..383079ac --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/AuthorizationRequestUriGenerator.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Utils; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Generates unique request URIs for authorization requests based on configured options. +/// This implementation uses cryptographic randomness to ensure that each URI is unique and secure. +/// +public class AuthorizationRequestUriGenerator : IAuthorizationRequestUriGenerator +{ + /// + /// Initializes a new instance of the class. + /// + /// The options to configure the behavior of the URI generation, + /// including the length of the URI. + public AuthorizationRequestUriGenerator(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Generates a unique request URI by appending a securely generated random string to a predefined URN prefix. + /// + /// A new unique URI for an authorization request. + public Uri GenerateRequestUri() + { + var randomBytes = CryptoRandom.GetRandomBytes(_options.Value.RequestUriLength); + return new(RequestUrn.Prefix + HttpServerUtility.UrlTokenEncode(randomBytes)); + } +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/ClientIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/ClientIdGenerator.cs new file mode 100644 index 00000000..6630ae8c --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/ClientIdGenerator.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Utils; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Implements the interface to generate client IDs for OpenID Connect (OIDC) clients. +/// The generated client IDs are based on cryptographically secure random bytes and are encoded in Base32 format, +/// providing a URL-safe, human-readable identifier. The length and format of the generated client IDs can be configured +/// through OIDC options. +/// +public class ClientIdGenerator : IClientIdGenerator +{ + /// + /// Initializes a new instance of the class, using the specified OIDC options + /// to configure the generation of client IDs. + /// + /// The OIDC options that determine the characteristics of the generated client IDs, + /// including their length and any other relevant configuration parameters. + public ClientIdGenerator(IOptions options) + { + _options = options; + } + + private readonly IOptions _options; + + /// + /// Generates a new client ID for an OIDC client. The method produces a random, URL-safe, and human-readable + /// identifier using Base32 encoding, based on the length specified in the OIDC options. This ensures that the + /// generated client IDs are suitable for use in various contexts, including web URLs and user interfaces. + /// + /// A new, randomly generated client ID string that conforms to the specifications defined in the + /// OIDC options. The client ID is encoded in Base32 format to ensure URL safety and readability. + public string GenerateClientId() + { + var desiredLength = _options.Value.NewClientOptions.ClientId.Length; + var randomBytes = CryptoRandom.GetRandomBytes((desiredLength + 4) * 5 / 8); + return Base32.EncodeHex(randomBytes, padding: false).ToLowerInvariant(); + } +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/ClientSecretGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/ClientSecretGenerator.cs new file mode 100644 index 00000000..0238b3b4 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/ClientSecretGenerator.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Provides a mechanism for securely generating client secret strings used in OAuth 2.0 and OpenID Connect authentication flows. +/// This implementation uses a cryptographic random number generator to produce a high-entropy secret string, +/// which is crucial for maintaining the security and integrity of client authentication. +/// The generated secret is encoded in a URL-safe Base32 format and trimmed to the specified length. +/// +public class ClientSecretGenerator : IClientSecretGenerator +{ + /// + /// Generates a client secret string with the specified length. + /// + /// The length of the client secret to generate. + /// The actual length of the generated secret might be slightly longer to ensure proper encoding + /// and then trimmed to the desired length. + /// A client secret string of the specified length. + public string GenerateClientSecret(int length) + => Base32.Encode(CryptoRandom.GetRandomBytes((length + 4) * 5 / 8), padding: false)[..length]; +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationCodeGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationCodeGenerator.cs new file mode 100644 index 00000000..a2ad5c04 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationCodeGenerator.cs @@ -0,0 +1,37 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Defines a contract for generating unique authorization codes for use in OAuth 2.0 authorization code flows. +/// Implementations of this interface should ensure that the generated codes are cryptographically secure +/// and suitable for one-time use in authenticating and authorizing access. +/// +public interface IAuthorizationCodeGenerator +{ + /// + /// Generates a unique, cryptographically secure authorization code. + /// + /// A string representing a unique authorization code. + string GenerateAuthorizationCode(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationRequestUriGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationRequestUriGenerator.cs new file mode 100644 index 00000000..3718ba9f --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/IAuthorizationRequestUriGenerator.cs @@ -0,0 +1,35 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Provides a mechanism to generate unique request URIs for storing and retrieving authorization requests. +/// +public interface IAuthorizationRequestUriGenerator +{ + /// + /// Generates a unique URI to be used as an identifier for an authorization request. + /// + /// A unique URI that serves as the identifier for a specific authorization request. + Uri GenerateRequestUri(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/IClientIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/IClientIdGenerator.cs new file mode 100644 index 00000000..a80d1123 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/IClientIdGenerator.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Defines an interface for generating client IDs for OpenID Connect (OIDC) clients. +/// This interface abstracts the mechanism for creating unique client identifiers used in the registration +/// of OIDC clients. Implementations of this interface can provide different strategies for generating client IDs, +/// such as UUIDs, random strings, or based on specific patterns. +/// +public interface IClientIdGenerator +{ + /// + /// Generates a new, unique client ID. This client ID is intended for use in identifying an OIDC client + /// within an authorization server or OIDC provider. The format and uniqueness constraints of the client ID + /// can vary depending on the implementation. + /// + /// A string representing the generated client ID, which should be unique across all clients + /// within the authorization server's context. + string GenerateClientId(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/IClientSecretGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/IClientSecretGenerator.cs new file mode 100644 index 00000000..ecfd5e79 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/IClientSecretGenerator.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Defines an interface responsible for generating secure client secrets for OpenID Connect (OIDC) clients. +/// Client secrets are used as credentials for client authentication to the OIDC provider or authorization server. +/// +public interface IClientSecretGenerator +{ + /// + /// Generates a new, secure client secret string of the specified length. The generated secret is intended + /// for use by confidential clients in OAuth 2.0 and OpenID Connect authentication flows. It is crucial + /// that the generated secret is of sufficient length and randomness to ensure the security of client + /// authentication processes. + /// + /// The desired length of the client secret. It is recommended that secrets be of + /// sufficient length (e.g., at least 32 characters) to ensure adequate security against brute-force + /// or guessing attacks. + /// A securely generated client secret string of the specified length. The secret should consist + /// of a cryptographically strong, random sequence of characters that can include a mix of letters, + /// digits, and special characters. + string GenerateClientSecret(int length); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/ISessionIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/ISessionIdGenerator.cs new file mode 100644 index 00000000..e4d2b568 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/ISessionIdGenerator.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Defines the interface for generating new session identifiers, which are crucial for tracking user sessions +/// in web applications, especially in scenarios involving authentication and authorization processes. +/// +public interface ISessionIdGenerator +{ + /// + /// Generates a new, unique session identifier. This method is responsible for producing session IDs that + /// are sufficiently random and unique to securely identify individual user sessions. The generated IDs + /// are used in session management mechanisms to differentiate between user sessions, thereby ensuring + /// that user data and interactions are isolated and protected across different sessions. + /// + /// A new, unique session identifier as a string. The format and characteristics of the session ID + /// (e.g., length, characters used) should be designed to enhance security and minimize the risk of session + /// hijacking or collision. + string GenerateSessionId(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/ITokenIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/ITokenIdGenerator.cs new file mode 100644 index 00000000..65f75988 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/ITokenIdGenerator.cs @@ -0,0 +1,38 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Defines an interface for generating unique identifiers for JSON Web Tokens (JWTs). +/// This interface abstracts the details of how JWT IDs are generated, allowing for different +/// implementations that can provide various methods of generating unique and secure token identifiers. +/// +public interface ITokenIdGenerator +{ + /// + /// Generates a new unique identifier for a JWT. The specific implementation determines the format + /// and characteristics of the generated ID, such as its length, randomness, and URL-safety. + /// + /// A unique identifier suitable for use as a JWT ID. + string GenerateTokenId(); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/SessionIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/SessionIdGenerator.cs new file mode 100644 index 00000000..7e793fb5 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/SessionIdGenerator.cs @@ -0,0 +1,43 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Implements the interface to generate unique session identifiers. +/// The session IDs are generated using a cryptographically strong random number generator and are encoded +/// to be safely included in HTTP URLs, avoiding characters that might cause issues in URLs. +/// +public class SessionIdGenerator : ISessionIdGenerator +{ + /// + /// Generates a new session identifier. The method employs a cryptographically strong random number generator + /// to produce a sequence of bytes, which are then URL-encoded to ensure they can be safely used within HTTP URLs. + /// This approach ensures that the session identifiers are highly unlikely to collide and are secure for use in + /// web applications. + /// + /// A string representing a URL-safe, cryptographically strong random session identifier. The identifier + /// is encoded in a way that makes it suitable for use in HTTP URLs, cookies, or any other URL-based contexts. + public string GenerateSessionId() => HttpServerUtility.UrlTokenEncode(CryptoRandom.GetRandomBytes(32)); +} diff --git a/Abblix.Oidc.Server/Features/RandomGenerators/TokenIdGenerator.cs b/Abblix.Oidc.Server/Features/RandomGenerators/TokenIdGenerator.cs new file mode 100644 index 00000000..c9635a35 --- /dev/null +++ b/Abblix.Oidc.Server/Features/RandomGenerators/TokenIdGenerator.cs @@ -0,0 +1,40 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.RandomGenerators; + +/// +/// Generates a new identifier for a JSON Web Token (JWT). This class uses a cryptographic-strength random number generator +/// to create a unique and secure identifier for each token. The generated identifier is then encoded using HTTP URL encoding +/// to ensure it is safe to transmit in URL contexts. +/// +public class TokenIdGenerator : ITokenIdGenerator +{ + /// + /// Creates a new unique identifier for a JWT. This method generates a 32-byte random number and encodes it using + /// HTTP URL-safe Base64 encoding, resulting in a string suitable for use as a JWT ID. + /// + /// A URL-safe, randomly generated unique identifier for a JWT. + public string GenerateTokenId() => HttpServerUtility.UrlTokenEncode(CryptoRandom.GetRandomBytes(32)); +} diff --git a/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..8ce57d2a --- /dev/null +++ b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs @@ -0,0 +1,381 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.DependencyInjection; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Implementation; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Oidc.Server.Features.ClientAuthentication; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Consents; +using Abblix.Oidc.Server.Features.Hashing; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.LogoutNotification; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Features.SessionManagement; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Features.Tokens.Revocation; +using Abblix.Oidc.Server.Features.Tokens.Validation; +using Abblix.Oidc.Server.Features.UserInfo; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server.Features; + +/// +/// Provides extension methods to for configuring OpenID Connect (OIDC) server services. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers client authentication services with the provided . + /// This setup includes various authenticators for supporting different client authentication methods + /// such as none, client secret post, client secret basic, private key JWT, and potentially others. + /// It enables the application to handle client authentication according to the OAuth 2.0 and OpenID Connect standards. + /// + /// The to add the client authentication services to. + /// The so that additional calls can be chained. + public static IServiceCollection AddClientAuthentication(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton() + .AddSingleton() + //.AddSingleton() //TODO support and uncomment + .AddSingleton() + .Compose(); + } + + /// + /// Configures services related to client information management. This includes registering the client information storage mechanism, + /// which serves as the provider and manager for client information, as well as the provider for client keys. This setup is crucial + /// for the OIDC server to manage and validate client identities and their corresponding secrets or keys. + /// + /// The to add the services to. + /// The so that additional calls can be chained. + public static IServiceCollection AddClientInformation(this IServiceCollection services) + { + return services + .AddSingleton() + .AddAlias() + .AddAlias() + .AddSingleton(); + } + + /// + /// Registers common services required by the application, like system clock, hashing services etc. + /// + /// The to add the services to. + /// The with the common services registered. + public static IServiceCollection AddCommonServices(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(TimeProvider.System); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + return services.AddJsonWebTokens(); + } + + /// + /// Configures the issuer provider service to dynamically determine the issuer URI based on application settings. + /// If an issuer is preconfigured in the options, a preconfigured issuer provider is used. + /// Otherwise, a request-based issuer provider is utilized to determine the issuer URI dynamically, + /// allowing for flexible deployment scenarios. + /// + /// The to add the issuer provider to. + /// The modified with the issuer provider configured. + public static IServiceCollection AddIssuer(this IServiceCollection services) + { + return services + .AddSingleton(sp => + { + var options = sp.GetRequiredService>().Value; + return options.Issuer != null + ? sp.CreateService() + : sp.CreateService(); + }); + } + + /// + /// Configures services for logout notification mechanisms within the application. This method + /// sets up both front-channel and back-channel logout capabilities, allowing the application to notify + /// clients about logout events through direct user agent redirection or server-to-server communication, respectively. + /// It integrates a composite logout notifier that aggregates both mechanisms to provide a unified approach to logout notifications. + /// + /// The to add the logout notification services to. + /// The so that additional calls can be chained. + public static IServiceCollection AddLogoutNotification(this IServiceCollection services) + { + return services + .AddFrontChannelLogout() + .AddBackChannelLogout() + .Compose(); + } + + /// + /// Adds the necessary services for back-channel logout functionality to the specified . + /// + public static IServiceCollection AddBackChannelLogout(this IServiceCollection services) + { + return services + .AddScoped() + .AddSingleton() + .AddHttpClient() + .Services; + } + + /// + /// Adds the necessary services for front-channel logout functionality to the specified . + /// Front-channel logout is typically used for web-based applications where the logout request is sent directly from + /// the user's browser to the identity provider and other logged-in services. + /// + public static IServiceCollection AddFrontChannelLogout(this IServiceCollection services) + { + return services + .AddScoped(); + } + + /// + /// Adds singleton services for generating random client IDs, client secrets, token IDs, and session IDs + /// to the specified . + /// + public static IServiceCollection AddRandomGenerators(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + return services; + } + + /// + /// Adds services related to session management and decorates the authorization request processor within + /// the specified . + /// + public static IServiceCollection AddSessionManagement(this IServiceCollection services) + { + return services + .AddScoped() + .Decorate(); + } + + /// + /// Configures token services including token creation, authentication, client-specific JWT handling, and + /// token revocation within the specified . + /// + /// + /// This method aggregates the setup of multiple services related to tokens, enhancing the application's security + /// infrastructure by providing comprehensive support for JWT (JSON Web Tokens) and token lifecycle management. + /// + /// It includes the configuration of: + /// - General token services for managing the creation and validation of tokens. + /// - Authentication services that leverage JWT for securing user authentication processes. + /// - Client JWT services, tailored for handling JWTs in client-specific contexts. + /// - Token revocation services to facilitate the process of invalidating tokens when necessary, + /// such as during logout or when a security breach is detected. + /// + /// The integration of these services ensures a robust and scalable approach to handling tokens, + /// which are critical for secure communication and access control within modern web applications. + /// + /// The to configure with token-related services. + /// The so that additional calls can be chained. + public static IServiceCollection AddTokenServices(this IServiceCollection services) + { + return services + .AddAccessToken() + .AddRefreshToken() + .AddIdentityToken() + .AddAuthServiceJwt() + .AddClientJwt() + .AddTokenRevocation(); + } + + + /// + /// This method adds a service that manages the lifecycle of refresh tokens, including their creation, + /// validation, and revocation. Refresh tokens are used to obtain new access tokens without requiring + /// the user to re-authenticate, enhancing the user experience by providing seamless session continuity. + /// + /// The to configure with token-related services. + /// The so that additional calls can be chained. + public static IServiceCollection AddRefreshToken(this IServiceCollection services) + { + return services + .AddSingleton(); + } + + /// + /// This method adds a service responsible for generating, validating, and managing access tokens. + /// Access tokens are crucial for securing API endpoints, as they provide a mechanism to verify that + /// a request is authorized to access specific resources. + /// + /// The to configure with token-related services. + /// The so that additional calls can be chained. + public static IServiceCollection AddAccessToken(this IServiceCollection services) + { + return services + .AddSingleton(); + } + + /// + /// This method adds a service that handles identity tokens, which are used to convey the identity of + /// the authenticated user to the application. Identity tokens typically contain claims about the user, + /// such as their name or role, which can be used for user interface customization and access control decisions. + /// + /// The to configure with token-related services. + /// The so that additional calls can be chained. + public static IServiceCollection AddIdentityToken(this IServiceCollection services) + { + return services + .AddSingleton(); + } + + /// + /// Registers JWT formatting and validation services for authentication within the specified . + /// + /// The to add the JWT authentication services to. + /// The for chaining further service registrations. + public static IServiceCollection AddAuthServiceJwt(this IServiceCollection services) + { + return services + .AddSingleton() + + .AddSingleton() + .AddSingleton(); + } + + /// + /// Registers a service for formatting JWTs specific to client authentication within the specified . + /// + /// + /// Adds a service which provides functionality for formatting JWTs used in client authentication scenarios. + /// This service ensures that JWTs generated for clients adhere to the required format and contain all necessary + /// claims for identifying and authenticating users in the client application. + /// + /// The to add the client JWT formatting service to. + /// The for chaining further service registrations. + public static IServiceCollection AddClientJwt(this IServiceCollection services) + { + return services + .AddSingleton(); + } + + /// + /// Decorates the JSON Web Token validator service with a token status validator to support token revocation + /// within the specified . + /// + /// + /// This method enhances the application's security by decorating the service + /// with . + /// This decoration adds the capability to check the revocation status of tokens, allowing the application to reject + /// tokens that have been revoked. This is crucial for maintaining the integrity and security of the application's + /// authentication system, particularly in response to security incidents or user logout events. + /// + /// The to add token revocation support to. + /// The for chaining further service registrations. + public static IServiceCollection AddTokenRevocation(this IServiceCollection services) + { + return services + .Decorate() + .AddSingleton(); + } + + /// + /// Registers the license JWT provider using options configuration to obtain the license JWT. + /// + /// + /// This method configures the OIDC service's licensing by using the , + /// which retrieves the license JWT from application settings or options. It's suitable for scenarios where + /// the license JWT is configured through application settings (e.g., appsettings.json or environment variables). + /// + /// The to add the license provider to. + /// The for chaining further configurations. + public static IServiceCollection AddLicenseFromOptions(this IServiceCollection services) + { + return services + .AddHostedService() + .AddSingleton(); + } + + /// + /// Registers the license JWT provider using a provided static license JWT string. + /// + /// + /// This method allows for direct specification of the license JWT, bypassing options configuration. + /// It utilizes the to supply the license JWT directly to the OIDC service. + /// This approach is particularly useful in scenarios where the license JWT is obtained programmatically or from + /// external sources not tied to the application's static configuration. + /// + /// The to add the license provider to. + /// The license JWT string to be used for OIDC service configuration validation. + /// The for chaining further configurations. + public static IServiceCollection AddLicense(this IServiceCollection services, string licenseJwt) + { + return services + .AddHostedService() + .AddSingleton(Dependency.Override(licenseJwt)); + } + + /// + /// Registers services for various storage functionalities related to the OAuth 2.0 and OpenID Connect flows within + /// the application. This method configures essential storage services that manage authorization codes and + /// authorization requests, ensuring their persistence and accessibility across the application. + /// + /// The to which the storage services will be added. + /// This collection is crucial for configuring dependency injection in ASP.NET Core applications, allowing services + /// to be added, managed, and retrieved throughout the application lifecycle. + /// The modified after adding the storage services, permitting additional + /// configurations to be chained. + public static IServiceCollection AddStorages(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + return services; + } + + /// + /// Registers services related to user claims management into the provided . + /// This method sets up essential services required for processing and handling user claims based on authentication + /// sessions and authorization requests, facilitating the integration of user-specific data into tokens or responses. + /// + /// The to which the user claims provider services will be + /// added. This collection is a mechanism for adding and retrieving dependencies in .NET applications, often used + /// to configure dependency injection in ASP.NET Core applications. + /// The updated after adding the services, allowing for further + /// modifications and additions to be chained. + public static IServiceCollection AddUserInfo(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + return services; + } +} diff --git a/Abblix.Oidc.Server/Features/SessionManagement/AuthorizationRequestProcessorDecorator.cs b/Abblix.Oidc.Server/Features/SessionManagement/AuthorizationRequestProcessorDecorator.cs new file mode 100644 index 00000000..125fc4a8 --- /dev/null +++ b/Abblix.Oidc.Server/Features/SessionManagement/AuthorizationRequestProcessorDecorator.cs @@ -0,0 +1,84 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.SessionManagement; + +/// +/// Enhances an existing authorization request processor with session management capabilities, +/// specifically tailored for OpenID Connect (OIDC) scenarios. This decorator introduces session +/// state handling into the authorization response, enabling clients to maintain and manage session +/// state in accordance with OpenID Connect session management specifications. +/// +public class AuthorizationRequestProcessorDecorator: IAuthorizationRequestProcessor +{ + /// + /// Constructs an instance of with a specified + /// authorization request processor and session management service. + /// + /// The authorization request processor to be enhanced with session management functionality. + /// The session management service responsible for generating and + /// handling session state information. + public AuthorizationRequestProcessorDecorator( + IAuthorizationRequestProcessor inner, + ISessionManagementService sessionManagementService) + { + _inner = inner; + _sessionManagementService = sessionManagementService; + } + + private readonly IAuthorizationRequestProcessor _inner; + private readonly ISessionManagementService _sessionManagementService; + + /// + /// Asynchronously processes an authorization request by delegating to the encapsulated authorization request processor, + /// and then enriches the authorization response with session state information when session management is enabled and applicable. + /// + /// The authorization request to be processed, expected to be a valid and authenticated request. + /// + /// A task that represents the asynchronous operation. Upon completion, the task yields an + /// that may include session state information to be used by the client for session management purposes. + /// + /// + /// This method ensures that responses to OpenID Connect authorization requests include session state information as by + /// the OpenID Connect session management specification. This allows clients to implement mechanisms for detecting session changes + /// and managing user sessions effectively. + /// + public async Task ProcessAsync(ValidAuthorizationRequest request) + { + var response = await _inner.ProcessAsync(request); + + // Append session state to the response if session management is enabled and the request qualifies + if (_sessionManagementService.Enabled && + response is SuccessfullyAuthenticated success && success.SessionId.HasValue() && + request.Model.Scope.HasFlag(Scopes.OpenId)) + { + success.SessionState = _sessionManagementService.GetSessionState(request.Model, success.SessionId); + } + + return response; + } +} diff --git a/Abblix.Oidc.Server/Features/SessionManagement/ISessionManagementService.cs b/Abblix.Oidc.Server/Features/SessionManagement/ISessionManagementService.cs new file mode 100644 index 00000000..032474dd --- /dev/null +++ b/Abblix.Oidc.Server/Features/SessionManagement/ISessionManagementService.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.SessionManagement; + +/// +/// Provides methods and properties for managing user sessions in the context of an OpenID Connect or OAuth 2.0 authorization server. +/// This interface enables the implementation of session management features, such as creating session cookies, +/// generating session state values and processing check session requests. +/// +public interface ISessionManagementService +{ + /// + /// Gets a value indicating whether session management is enabled and available for use in the authorization server. + /// When true, session management features are active and can be utilized to track and manage user sessions. + /// + bool Enabled { get; } + + /// + /// Retrieves the session cookie that is used for managing the user's session. This cookie can be used to persist session + /// information across browser requests and to facilitate session management operations. + /// + /// A object configured for session management, containing details such as the cookie name, + /// value, path, and security settings. + Cookie GetSessionCookie(); + + /// + /// Generates a session state string that represents the current state of the user's session. This string can be used + /// to validate the session's integrity and to detect session changes or logout events in a front-channel logout scenario. + /// + /// The authorization request context, which may contain parameters influencing the session state generation. + /// A unique identifier for the user's session. This ID is used as part of the session state generation process. + /// A session state string that represents the hashed state of the session, including the session ID and other relevant factors. + string GetSessionState(AuthorizationRequest request, string sessionId); + + /// + /// Asynchronously retrieves the response for a session check operation. This method is called to process check session requests, + /// allowing the client to query the current state of the user's session and detect any changes. + /// + /// A representing the asynchronous operation, yielding a object that + /// contains the necessary information for the client to evaluate the session state. + Task GetCheckSessionResponseAsync(); +} diff --git a/Abblix.Oidc.Server/Features/SessionManagement/Resources/checkSession.html b/Abblix.Oidc.Server/Features/SessionManagement/Resources/checkSession.html new file mode 100644 index 00000000..84f323c6 --- /dev/null +++ b/Abblix.Oidc.Server/Features/SessionManagement/Resources/checkSession.html @@ -0,0 +1,66 @@ + + + + + + + diff --git a/Abblix.Oidc.Server/Features/SessionManagement/SessionManagementService.cs b/Abblix.Oidc.Server/Features/SessionManagement/SessionManagementService.cs new file mode 100644 index 00000000..76c6fca0 --- /dev/null +++ b/Abblix.Oidc.Server/Features/SessionManagement/SessionManagementService.cs @@ -0,0 +1,130 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Text; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces; +using Abblix.Oidc.Server.Model; +using Abblix.Utils; +using Microsoft.Extensions.Options; +using static System.Web.HttpUtility; +using static Abblix.Utils.HttpServerUtility; + + +namespace Abblix.Oidc.Server.Features.SessionManagement; +/// +/// Implements session management functionality in accordance with OpenID Connect session management standards. +/// This service is responsible for managing browser sessions by utilizing cookies and providing mechanisms +/// to check and maintain the session state between the client and the server. +/// +public class SessionManagementService : ISessionManagementService +{ + private const string CookieNamePlaceHolder = "\"{{cookieName}}\""; + + /// + /// Initializes a new instance of the class with the specified + /// OpenID Connect options and request information provider. + /// + /// The options for configuring the OpenID Connect session management service. + /// The provider for accessing request-related information, such as whether + /// the current request is over HTTPS and the request's base path. + public SessionManagementService( + IOptionsSnapshot options, + IRequestInfoProvider requestInfoProvider) + { + _options = options; + _requestInfoProvider = requestInfoProvider; + } + + private readonly IOptionsSnapshot _options; + private readonly IRequestInfoProvider _requestInfoProvider; + + /// + /// Indicates whether session management functionality is enabled based on the configured endpoints. + /// + public bool Enabled => _options.Value.EnabledEndpoints.HasFlag(OidcEndpoints.CheckSession); + + /// + /// Retrieves a cookie configured for session management. This cookie can be used to track the session state + /// between the client and the server. + /// + /// A object configured with session management settings, such as the cookie name, + /// domain, path, and security attributes. + public Cookie GetSessionCookie() + { + var options = _options.Value.CheckSessionCookie; + return new Cookie( + options.Name, + new CookieOptions + { + HttpOnly = false, + IsEssential = true, + Secure = _requestInfoProvider.IsHttps, + Path = _requestInfoProvider.PathBase, + Domain = options.Domain, + SameSite = options.SameSite, + }); + } + + /// + /// Generates a session state string for an authorization request. This string can be used by the client to + /// validate the session state. + /// + /// The authorization request containing client and redirect URI information. + /// A unique identifier for the session. + /// A session state string composed of the client ID, origin, session ID, and a salt value, hashed for security. + public string GetSessionState(AuthorizationRequest request, string sessionId) + { + var origin = request.RedirectUri.NotNull(nameof(request.RedirectUri)).GetOrigin(); + var salt = CryptoRandom.GetRandomBytes(16).ToHexString(); + var sessionState = string.Join(" ", request.ClientId, origin, sessionId, salt); + var hash = UrlTokenEncode(SHA256.HashData(Encoding.UTF8.GetBytes(sessionState))); + return string.Join(".", hash, salt); + } + + /// + /// Asynchronously generates the response content for the check session endpoint. This method retrieves an HTML template + /// that includes JavaScript code for the client to check the session state. + /// + /// A task that represents the asynchronous operation, resulting in a + /// containing the HTML content for the check session iframe and the name of the session management cookie. + public async Task GetCheckSessionResponseAsync() + { + var type = typeof(SessionManagementService); + var name = $"{type.Namespace}.Resources.checkSession.html"; + + string htmlTemplate; + await using (var stream = type.Assembly.GetManifestResourceStream(name).NotNull(name)) + using (var reader = new StreamReader(stream, Encoding.UTF8)) + htmlTemplate = await reader.ReadToEndAsync(); + + var cookieName = _options.Value.CheckSessionCookie.Name; + var htmlContent = htmlTemplate.Replace( + CookieNamePlaceHolder, + JavaScriptStringEncode(cookieName, true)); + + return new CheckSessionResponse(htmlContent, cookieName); + } +} diff --git a/Abblix.Oidc.Server/Features/Storages/AuthorizationCodeService.cs b/Abblix.Oidc.Server/Features/Storages/AuthorizationCodeService.cs new file mode 100644 index 00000000..6231bef9 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/AuthorizationCodeService.cs @@ -0,0 +1,127 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.RandomGenerators; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides services for managing the lifecycle of OAuth 2.0 authorization codes. This service generates, stores, +/// validates, and deletes authorization codes as part of the authorization code grant flow. +/// +public class AuthorizationCodeService : IAuthorizationCodeService +{ + /// + /// Initializes a new instance of the class, + /// configuring it with the necessary components for authorization code generation and storage. + /// + /// The generator that creates unique authorization codes. + /// The storage mechanism for persisting and retrieving authorization codes and their + /// associated data. + public AuthorizationCodeService( + IAuthorizationCodeGenerator authorizationCodeGenerator, + IEntityStorage storage) + { + _authorizationCodeGenerator = authorizationCodeGenerator; + _storage = storage; + } + + private readonly IAuthorizationCodeGenerator _authorizationCodeGenerator; + private readonly IEntityStorage _storage; + + /// + /// Generates a unique authorization code for a given authorization grant result and client information. + /// This code is subsequently used by the client to request an access token. + /// + /// An object encapsulating the result of the authorization grant, including + /// user authentication session and authorization context details. + /// + /// A task that resolves to the generated authorization code as a string. + public async Task GenerateAuthorizationCodeAsync( + AuthorizedGrant authorizedGrant, + TimeSpan authorizationCodeExpiresIn) + { + var authorizationCode = _authorizationCodeGenerator.GenerateAuthorizationCode(); + + await _storage.SetAsync( + ToKeyString(authorizationCode), + authorizedGrant, + new StorageOptions { AbsoluteExpirationRelativeToNow = authorizationCodeExpiresIn }); + + return authorizationCode; + } + + /// + /// Validates and processes an authorization code, ensuring it is correct and has not expired or been used previously. + /// + /// The authorization code to validate and process. + /// A task that resolves to a , which indicates the outcome of + /// the authorization attempt and contains any tokens issued. + public async Task AuthorizeByCodeAsync(string authorizationCode) + { + var result = await _storage.GetAsync(ToKeyString(authorizationCode), false); + if (result == null) + { + return new InvalidGrantResult(ErrorCodes.InvalidGrant, "Authorization code is invalid"); + } + + return result; + } + + /// + /// Removes an authorization code from storage, ensuring that it cannot be reused. + /// + /// The authorization code to remove. + /// A task representing the asynchronous operation to remove the code. + public Task RemoveAuthorizationCodeAsync(string authorizationCode) + => _storage.RemoveAsync(ToKeyString(authorizationCode)); + + /// + /// Updates the authorization grant result based on a specific authorization code and client information. + /// This method allows the authorization grant to be updated with new information or tokens as needed. + /// + /// The authorization code associated with the grant result to update. + /// The updated authorization grant result containing the latest + /// authentication and authorization details. + /// + /// A task representing the asynchronous operation of updating the authorization grant result. + public Task UpdateAuthorizationGrantAsync( + string authorizationCode, + AuthorizedGrant authorizedGrant, + TimeSpan authorizationCodeExpiresIn) + { + return _storage.SetAsync( + ToKeyString(authorizationCode), + authorizedGrant, + new StorageOptions { AbsoluteExpirationRelativeToNow = authorizationCodeExpiresIn } + ); + } + + /// + /// Converts an authorization code into a standardized key string for use in storage. + /// + /// The authorization code to convert. + /// A string that represents the standardized key for the authorization code. + private string ToKeyString(string authorizationCode) => $"{nameof(authorizationCode)}:{authorizationCode}"; +} diff --git a/Abblix.Oidc.Server/Features/Storages/AuthorizationRequestStorage.cs b/Abblix.Oidc.Server/Features/Storages/AuthorizationRequestStorage.cs new file mode 100644 index 00000000..198a241d --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/AuthorizationRequestStorage.cs @@ -0,0 +1,91 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides storage and retrieval services for OAuth 2.0 authorization requests using a distributed cache. +/// This class is designed to handle the storage of objects and facilitate +/// their retrieval using unique request URIs, supporting scenarios such as the OAuth 2.0 Pushed Authorization Requests +/// (PAR). +/// +public class AuthorizationRequestStorage : IAuthorizationRequestStorage +{ + /// + /// Initializes a new instance of the class, + /// setting up necessary dependencies for storing and retrieving authorization requests. + /// + /// The generator used to create unique URIs for each authorization + /// request. + /// The persistent storage mechanism where authorization requests are stored. + public AuthorizationRequestStorage( + IAuthorizationRequestUriGenerator authorizationRequestUriGenerator, + IEntityStorage storage) + { + _authorizationRequestUriGenerator = authorizationRequestUriGenerator; + _storage = storage; + } + + private readonly IAuthorizationRequestUriGenerator _authorizationRequestUriGenerator; + private readonly IEntityStorage _storage; + + /// + /// Stores an authorization request in the distributed cache with a generated URI and a specified expiration time. + /// + /// The authorization request to store. + /// The duration after which the stored request will expire and no longer be valid. This + /// duration is typically dictated by the OAuth 2.0 server's configuration and the nature of the client's request. + /// + /// A task that resolves to a , which includes the stored request + /// and its unique URI. This response is used to facilitate subsequent authorization processes that may require + /// accessing the request via its URI. + public async Task StoreAsync(AuthorizationRequest request, TimeSpan expiresIn) + { + var requestUri = _authorizationRequestUriGenerator.GenerateRequestUri(); + + await _storage.SetAsync( + ToKeyString(requestUri), + request, + new StorageOptions { AbsoluteExpirationRelativeToNow = expiresIn }); + + return new PushedAuthorizationResponse(request, requestUri, expiresIn); + } + + /// + /// Attempts to retrieve an authorization request from the distributed cache using its unique URI. + /// Optionally removes the request from the cache depending on the parameter. + /// + /// The URI associated with the authorization request. + /// Whether to remove the request from the cache after retrieving it. This is typically + /// true for operations where the request is intended for single retrieval, such as after redirecting a client + /// to an authorization endpoint. + /// A task that resolves to the retrieved if found; otherwise, null. + /// If the request is not found, it may have expired or been removed from the cache previously. + public Task TryGetAsync(Uri requestUri, bool shouldRemove) + => _storage.GetAsync(ToKeyString(requestUri), shouldRemove); + + private static string ToKeyString(Uri requestUri) => requestUri.OriginalString; +} diff --git a/Abblix.Oidc.Server/Features/Storages/DistributedCacheStorage.cs b/Abblix.Oidc.Server/Features/Storages/DistributedCacheStorage.cs new file mode 100644 index 00000000..f82f7ac2 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/DistributedCacheStorage.cs @@ -0,0 +1,110 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Interfaces; +using Microsoft.Extensions.Caching.Distributed; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides a general-purpose distributed caching mechanism with serialization support, +/// enabling the storage and retrieval of any type of serialized objects. +/// +public sealed class DistributedCacheStorage : IEntityStorage +{ + /// + /// Initializes a new instance of the class. + /// + /// The distributed cache backend used for storing and retrieving data. + /// The serializer used for converting objects to and from binary format. + public DistributedCacheStorage(IDistributedCache cache, IBinarySerializer serializer) + { + _cache = cache; + _serializer = serializer; + } + + private readonly IDistributedCache _cache; + private readonly IBinarySerializer _serializer; + + /// + /// Asynchronously stores an object in the distributed cache. + /// + /// The type of the object to store. + /// The key under which the object is stored. + /// The object to store. + /// Configuration options for the cache entry, such as expiration. + /// An optional cancellation token to cancel the operation. + /// A task that represents the asynchronous operation. + public Task SetAsync(string key, T value, StorageOptions options, CancellationToken? token = null) + { + ArgumentNullException.ThrowIfNull(key); + return _cache.SetAsync( + key, + _serializer.Serialize(value), + new DistributedCacheEntryOptions + { + AbsoluteExpiration = options.AbsoluteExpiration, + AbsoluteExpirationRelativeToNow = options.AbsoluteExpirationRelativeToNow, + SlidingExpiration = options.SlidingExpiration, + }, + token ?? CancellationToken.None); + } + + /// + /// Asynchronously retrieves an object from the distributed cache. + /// + /// The type of the object to retrieve. + /// The key associated with the object to retrieve. + /// Whether to remove the object from the cache after retrieval. + /// An optional cancellation token to cancel the operation. + /// A task that represents the asynchronous operation, containing the retrieved object, if found; + /// otherwise, null. + public async Task GetAsync(string key, bool removeOnRetrieval, CancellationToken? token = null) + { + ArgumentNullException.ThrowIfNull(key); + token ??= CancellationToken.None; + + var result = await _cache.GetAsync(key, token.Value); + if (result == null) + { + return default; + } + + var deserializedResult = _serializer.Deserialize(result); + + if (removeOnRetrieval) + { + await _cache.RemoveAsync(key, token.Value); + } + + return deserializedResult; + } + + /// + /// Asynchronously removes an object from the distributed cache. + /// + /// The key of the object to remove. + /// An optional cancellation token to cancel the operation. + /// A task that represents the asynchronous operation of removing the object. + public Task RemoveAsync(string key, CancellationToken? token = null) + => _cache.RemoveAsync(key, token ?? CancellationToken.None); +} diff --git a/Abblix.Oidc.Server/Features/Storages/IAuthorizationCodeService.cs b/Abblix.Oidc.Server/Features/Storages/IAuthorizationCodeService.cs new file mode 100644 index 00000000..94497c24 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/IAuthorizationCodeService.cs @@ -0,0 +1,82 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides a contract for managing OAuth 2.0 authorization codes, facilitating the authorization code flow. +/// This interface enables the generation of unique authorization codes for authenticated sessions, the validation +/// of these codes for user authorization, and the subsequent removal of codes once they have been used or expired, +/// ensuring adherence to the OAuth 2.0 specification. +/// +public interface IAuthorizationCodeService +{ + /// + /// Generates a unique authorization code for a given authorization grant result and specified expiration time. + /// The authorization code is a temporary code that the client exchanges for an access token, typically after + /// the user has authenticated and authorized the request. + /// + /// An object encapsulating the result of the authorization grant, including + /// user authentication session and authorization context details. + /// The duration after which the generated authorization code will expire. + /// A task that asynchronously returns the generated authorization code as a string. This code + /// is intended for single-use and has a limited lifetime, after which it must be exchanged for an access token + /// or considered invalid. + Task GenerateAuthorizationCodeAsync( + AuthorizedGrant authorizedGrant, + TimeSpan authorizationCodeExpiresIn); + + /// + /// Validates an authorization code and processes the authorization request, authorizing the user + /// and granting access based on the code provided. This method verifies the code's validity, ensuring it + /// matches a previously issued code and has not expired or been used. + /// + /// The authorization code to be validated and processed for granting access. + /// A task that asynchronously returns a representing the outcome + /// of the authorization process, including any access tokens or refresh tokens issued as part of the grant. + Task AuthorizeByCodeAsync(string authorizationCode); + + /// + /// Asynchronously removes an authorization code from the system. This method is typically called once + /// an authorization code has been exchanged for an access token, or when it expires, ensuring that the code + /// cannot be reused. + /// + /// The authorization code to be removed. + /// A task representing the asynchronous operation of removing the authorization code. + Task RemoveAuthorizationCodeAsync(string authorizationCode); + + /// + /// Updates the authorization grant result based on a specific authorization code and expiration time. + /// This method allows the authorization grant to be updated with new information or tokens as needed. + /// + /// The authorization code associated with the grant result to update. + /// The updated authorization grant result containing the latest + /// authentication and authorization details. + /// The duration after which the updated authorization code will expire. + /// A task representing the asynchronous operation of updating the authorization grant result. + Task UpdateAuthorizationGrantAsync( + string authorizationCode, + AuthorizedGrant authorizedGrant, + TimeSpan authorizationCodeExpiresIn); +} diff --git a/Abblix.Oidc.Server/Features/Storages/IAuthorizationRequestStorage.cs b/Abblix.Oidc.Server/Features/Storages/IAuthorizationRequestStorage.cs new file mode 100644 index 00000000..2e0df6cb --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/IAuthorizationRequestStorage.cs @@ -0,0 +1,63 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides mechanisms for securely storing and retrieving OAuth 2.0 authorization requests. +/// This interface abstracts the storage layer, allowing for implementation-specific details +/// such as database, cache or filesystem storage. +/// +public interface IAuthorizationRequestStorage +{ + /// + /// Asynchronously stores the provided authorization request in a secure manner and returns a unique identifier + /// for it. This identifier can be used to retrieve the request at a later time, facilitating mechanisms + /// like the Pushed Authorization Request (PAR). This method also accepts an expiration time for the request, + /// allowing the storage mechanism to automatically invalidate the request after a certain period. + /// + /// The instance to be stored. + /// The duration after which the stored request should expire and be considered invalid. + /// A that, when completed successfully, + /// yields a containing the unique identifier of the stored request + /// and its expiration information. + /// + Task StoreAsync(AuthorizationRequest request, TimeSpan expiresIn); + + /// + /// Asynchronously retrieves an authorization request using a previously stored unique identifier. + /// This method facilitates the retrieval of authorization requests for further processing or validation. + /// The shouldRemove parameter controls whether the request is deleted from storage upon retrieval, + /// ensuring it cannot be retrieved again, which is essential for one-time use scenarios like authorization codes. + /// + /// The unique identifier of the authorization request, typically a URI, + /// used to locate the request in storage. + /// Specifies whether the request should be removed from storage on retrieval. + /// This is useful for one-time use scenarios, ensuring that an authorization request cannot be reused. + /// A that, when completed successfully, yields + /// the associated with the specified identifier, + /// or null if no such request exists or if it has expired. + Task TryGetAsync(Uri requestUri, bool shouldRemove = false); +} diff --git a/Abblix.Oidc.Server/Features/Storages/IEntityStorage.cs b/Abblix.Oidc.Server/Features/Storages/IEntityStorage.cs new file mode 100644 index 00000000..03932480 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/IEntityStorage.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides an interface for a storage mechanism that can store entities of various types persistently. +/// This interface enables asynchronous interactions with the underlying storage system, allowing +/// operations such as adding, retrieving, and removing entities with flexible caching strategies. +/// +public interface IEntityStorage +{ + /// + /// Asynchronously adds an entity to the storage with the specified cache entry options. + /// + /// The type of the entity to cache. + /// The unique cache key used to store and retrieve the value. + /// The entity to be stored in the cache. + /// Configuration options for the cache entry, such as expiration times. + /// An optional cancellation token that can be used to cancel the storage operation. + /// A task that represents the asynchronous operation, providing awareness of completion or faults. + Task SetAsync(string key, T value, StorageOptions options, CancellationToken? token = null); + + /// + /// Asynchronously retrieves an entity of specified type from the storage. + /// + /// The type of the entity to retrieve. + /// The unique cache key associated with the entity to retrieve. + /// Indicates whether the entity should be removed from the storage after retrieval. + /// An optional cancellation token that can be used to cancel the retrieval operation. + /// A task that returns the retrieved entity, if found, or null if no entity is found. + Task GetAsync(string key, bool removeOnRetrieval, CancellationToken? token = null); + + /// + /// Asynchronously removes an entity from the storage. + /// + /// The unique cache key associated with the entity to be removed. + /// An optional cancellation token that can be used to cancel the removal operation. + /// A task that represents the asynchronous operation of removing the entity. + Task RemoveAsync(string key, CancellationToken? token = null); +} diff --git a/Abblix.Oidc.Server/Features/Storages/ITokenRegistry.cs b/Abblix.Oidc.Server/Features/Storages/ITokenRegistry.cs new file mode 100644 index 00000000..07aecda1 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/ITokenRegistry.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.Tokens.Revocation; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Represents a registry that manages the statuses of JSON Web Tokens (JWTs). This registry allows for tracking +/// and updating the status of tokens, such as marking them as used or revoked. +/// +public interface ITokenRegistry +{ + /// + /// Asynchronously retrieves the current status of a specified JWT identifier. + /// + /// The identifier of the JWT whose status is to be queried. + /// A task that, when completed successfully, returns the + /// indicating the current status of the JWT (e.g., active, revoked). + Task GetStatusAsync(string jwtId); + + /// + /// Asynchronously sets the status for a specified JWT identifier. This operation may be used to mark a token + /// as used or revoked. + /// + /// The identifier of the JWT whose status is to be updated. + /// The new status to be assigned to the JWT, such as or + /// . + /// The expiration time of the JWT. This is used to determine if the status should be + /// updated based on the token's validity. + /// A task representing the asynchronous operation of updating the token's status. + Task SetStatusAsync(string jwtId, JsonWebTokenStatus status, DateTimeOffset expiresAt); +} diff --git a/Abblix.Oidc.Server/Features/Storages/StorageOptions.cs b/Abblix.Oidc.Server/Features/Storages/StorageOptions.cs new file mode 100644 index 00000000..021d34f8 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/StorageOptions.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Provides configuration settings for cache entry behaviors in storage operations. +/// +public record StorageOptions +{ + /// + /// The absolute expiration date and time for the cache entry. If set, the entry will expire and be removed + /// from the cache at this specific date and time. + /// + public DateTimeOffset? AbsoluteExpiration { get; init; } + + /// + /// The absolute expiration time relative to now. If set, the entry will expire after the specified duration + /// from the time it was added or updated. + /// + public TimeSpan? AbsoluteExpirationRelativeToNow { get; init; } + + /// + /// The sliding expiration time. If set, the expiration time for the cache entry will be extended by this amount + /// each time the entry is accessed, preventing the entry from expiring if it is frequently accessed. + /// + public TimeSpan? SlidingExpiration { get; init; } +} diff --git a/Abblix.Oidc.Server/Features/Storages/TokenRegistry.cs b/Abblix.Oidc.Server/Features/Storages/TokenRegistry.cs new file mode 100644 index 00000000..06a91279 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Storages/TokenRegistry.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.Tokens.Revocation; + +namespace Abblix.Oidc.Server.Features.Storages; + +/// +/// Manages the registration and tracking of JSON Web Token (JWT) statuses within a persistent storage. +/// This class is responsible for determining the current status of JWTs, such as whether they are active, +/// revoked, or expired, and updating these statuses as necessary. +/// +public class TokenRegistry : ITokenRegistry +{ + /// + /// Initializes a new instance of the class. + /// + /// The storage mechanism that will handle the persistence of token statuses. + public TokenRegistry(IEntityStorage storage) + { + _storage = storage; + } + + private readonly IEntityStorage _storage; + + + /// + /// Retrieves the current status of a JWT by its identifier. + /// + /// The identifier of the JWT whose status is being queried. + /// A task that resolves to the status of the JWT if found; otherwise, a default status indicating + /// the token does not exist in the registry. + public Task GetStatusAsync(string jwtId) + => _storage.GetAsync(ToKeyString(jwtId), false); + + /// + /// Sets or updates the status of a JWT by its identifier, with an expiration on the status entry. + /// + /// The identifier of the JWT whose status is being set. + /// The status to assign to the JWT. + /// The time at which the status record should expire. + /// A task representing the asynchronous operation of setting the token's status. + public Task SetStatusAsync(string jwtId, JsonWebTokenStatus status, DateTimeOffset expiresAt) + => _storage.SetAsync(ToKeyString(jwtId), status, new StorageOptions { AbsoluteExpiration = expiresAt }); + + private static string ToKeyString(string jwtId) => $"{nameof(jwtId)}:{jwtId}"; +} diff --git a/Abblix.Oidc.Server/Features/Tokens/AccessTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/AccessTokenService.cs new file mode 100644 index 00000000..1038a27a --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/AccessTokenService.cs @@ -0,0 +1,139 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Manages the lifecycle of access tokens for authenticated users, facilitating the creation of tokens with embedded +/// authorization details and the authentication of requests using these tokens. Utilizes issuer information, +/// current time, unique token identifiers, and JWT formatting to generate secure and compliant access tokens. +/// +internal class AccessTokenService : IAccessTokenService +{ + /// + /// Initializes a new instance of the , responsible for generating and validating + /// access tokens. + /// + /// The provider responsible for determining the issuer (iss) claim in the token, + /// which identifies the authorization server that issued the token. + /// The service used to obtain the current time, ensuring accurate token expiration and + /// issuance timestamps. + /// The service responsible for generating unique identifiers (jti) for each token, + /// enhancing security by enabling token revocation and tracking capabilities. + /// The formatter used for encoding the JSON Web Token (JWT), ensuring it meets + /// the standards required for secure transmission and validation. + public AccessTokenService( + IIssuerProvider issuerProvider, + TimeProvider clock, + ITokenIdGenerator tokenIdGenerator, + IAuthServiceJwtFormatter serviceJwtFormatter) + { + _issuerProvider = issuerProvider; + _clock = clock; + _tokenIdGenerator = tokenIdGenerator; + _serviceJwtFormatter = serviceJwtFormatter; + } + + private readonly IIssuerProvider _issuerProvider; + private readonly TimeProvider _clock; + private readonly ITokenIdGenerator _tokenIdGenerator; + private readonly IAuthServiceJwtFormatter _serviceJwtFormatter; + + /// + /// Asynchronously generates a new access token incorporating the authentication session and authorization context + /// of the user, along with client-specific settings. This token is crafted using standard JWT practices, + /// ensuring it aligns with OAuth 2.0 and OpenID Connect requirements. + /// + /// Details of the user's current authentication session, including subject and + /// authentication time. + /// Context providing authorization details such as requested scopes and permissions. + /// + /// Client-specific information, including token expiration settings and required JWT + /// algorithms. + /// A task that resolves to an , representing the newly minted access + /// token. + /// + /// The generated access token includes a unique identifier and timestamps to manage its lifecycle. It also encodes + /// the issuer's information, ensuring that the token can be validated against the issuing authority. This method + /// leverages provided services to dynamically generate compliant tokens suited for various authorization needs. + /// + public async Task CreateAccessTokenAsync( + AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo) + { + var issuedAt = _clock.GetUtcNow(); + + var accessToken = new JsonWebToken + { + Header = + { + Type = JwtTypes.AccessToken, + Algorithm = SigningAlgorithms.RS256, + }, + Payload = + { + JwtId = _tokenIdGenerator.GenerateTokenId(), + IssuedAt = issuedAt, + NotBefore = issuedAt, + ExpiresAt = issuedAt + clientInfo.AccessTokenExpiresIn, + Issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()), + Audiences = new[] { clientInfo.ClientId }, + }, + }; + + authSession.ApplyTo(accessToken.Payload); + authContext.ApplyTo(accessToken.Payload); + + return new EncodedJsonWebToken(accessToken, await _serviceJwtFormatter.FormatAsync(accessToken)); + } + + /// + /// Validates the provided access token and extracts the associated authentication session and authorization context. + /// This process authenticates the token bearer and retrieves their authorization details, enabling secure resource + /// access. + /// + /// The access token to be authenticated and analyzed. + /// A task that, when completed, yields a tuple containing the and + /// derived from the token. + /// + /// This method facilitates the secure validation of access tokens, ensuring that only tokens issued by the trusted + /// authority and not tampered with are accepted. It decodes embedded claims to reconstruct the original + /// authorization and authentication details, supporting secure and informed access control decisions. + /// + public Task<(AuthSession, AuthorizationContext)> AuthenticateByAccessTokenAsync(JsonWebToken accessToken) + { + var authSession = accessToken.Payload.ToAuthSession(); + var authorizationContext = accessToken.Payload.ToAuthorizationContext(); + return Task.FromResult((authSession, authorizationContext)); + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/EncodedJsonWebToken.cs b/Abblix.Oidc.Server/Features/Tokens/EncodedJsonWebToken.cs new file mode 100644 index 00000000..93ccbf5e --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/EncodedJsonWebToken.cs @@ -0,0 +1,32 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Represents an encoded JSON Web Token (JWT) along with its decoded model representation. +/// +/// The decoded model representation of the JWT. +/// The encoded string form of the JWT. +public record EncodedJsonWebToken(JsonWebToken Token, string EncodedJwt); diff --git a/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs b/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs new file mode 100644 index 00000000..779a9c35 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs @@ -0,0 +1,71 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.Tokens.Formatters; + +/// +/// Provides functionality to format and sign JSON Web Tokens (JWTs) specifically for use within the authentication service. +/// This class leverages signing and optional encryption to generate JWTs that authenticate and authorize internal service operations. +/// +public class AuthServiceJwtFormatter : IAuthServiceJwtFormatter +{ + public AuthServiceJwtFormatter( + IJsonWebTokenCreator jwtCreator, + IAuthServiceKeysProvider serviceKeysProvider) + { + _jwtCreator = jwtCreator; + _serviceKeysProvider = serviceKeysProvider; + } + + private readonly IJsonWebTokenCreator _jwtCreator; + private readonly IAuthServiceKeysProvider _serviceKeysProvider; + + /// + /// Formats and signs a JWT for use by the authentication service, applying the appropriate cryptographic operations + /// based on the JWT's specified requirements and the available cryptographic keys. + /// + /// The JSON Web Token (JWT) to be formatted and signed, potentially also encrypted. + /// A that represents the asynchronous operation, resulting in the JWT formatted as a string. + /// + /// This method selects the appropriate signing key based on the algorithm specified in the JWT's header. + /// If encryption is supported and keys are available, it also encrypts the JWT. The result is a JWT string + /// that is ready for use in authenticating and authorizing service operations. + /// + public async Task FormatAsync(JsonWebToken token) + { + // Select the appropriate signing key based on the JWT specified algorithm + var signingCredentials = await _serviceKeysProvider.GetSigningKeys(true) + .FirstByAlgorithmAsync(token.Header.Algorithm); + + // Optionally select an encryption key if available + var encryptingCredentials = await _serviceKeysProvider.GetEncryptionKeys() + .FirstOrDefaultAsync(); + + // Issue the JWT with the selected signing and encryption credentials + return await _jwtCreator.IssueAsync(token, signingCredentials, encryptingCredentials); + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Formatters/ClientJwtFormatter.cs b/Abblix.Oidc.Server/Features/Tokens/Formatters/ClientJwtFormatter.cs new file mode 100644 index 00000000..b062ee7b --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Formatters/ClientJwtFormatter.cs @@ -0,0 +1,73 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.Tokens.Formatters; + +/// +/// Provides functionality to format JSON Web Tokens (JWTs) issued to clients by the authentication service. +/// This class handles the signing of JWTs and, if configured, their encryption, based on the needs of each client. +/// +public class ClientJwtFormatter : IClientJwtFormatter +{ + public ClientJwtFormatter( + IJsonWebTokenCreator jwtCreator, + IClientKeysProvider clientKeysProvider, + IAuthServiceKeysProvider serviceKeysProvider) + { + _jwtCreator = jwtCreator; + _clientKeysProvider = clientKeysProvider; + _serviceKeysProvider = serviceKeysProvider; + } + + private readonly IJsonWebTokenCreator _jwtCreator; + private readonly IClientKeysProvider _clientKeysProvider; + private readonly IAuthServiceKeysProvider _serviceKeysProvider; + + /// + /// Asynchronously formats a JWT for a specific client, applying the necessary cryptographic operations + /// based on the client's configuration and the authentication service's capabilities. + /// + /// The JSON Web Token (JWT) to be formatted for the client. + /// Information about the client to which the JWT is issued, including any requirements for encryption. + /// A that represents the asynchronous operation, resulting in a JWT string formatted and ready for use by the client. + /// + /// This method ensures the JWT is signed with the appropriate key from the authentication service. If the client's configuration supports encryption, + /// the method also encrypts the JWT using the client's public key. The result is a JWT that conforms to the security requirements of both + /// the client and the authentication service. + /// + public async Task FormatAsync(JsonWebToken token, ClientInfo clientInfo) + { + var signingCredentials = await _serviceKeysProvider.GetSigningKeys(true) + .FirstByAlgorithmAsync(token.Header.Algorithm); + + var encryptingCredentials = await _clientKeysProvider.GetEncryptionKeys(clientInfo) + .FirstOrDefaultAsync(); + + return await _jwtCreator.IssueAsync(token, signingCredentials, encryptingCredentials); + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Formatters/IAuthServiceJwtFormatter.cs b/Abblix.Oidc.Server/Features/Tokens/Formatters/IAuthServiceJwtFormatter.cs new file mode 100644 index 00000000..cfc1eead --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Formatters/IAuthServiceJwtFormatter.cs @@ -0,0 +1,39 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + +namespace Abblix.Oidc.Server.Features.Tokens.Formatters; + +/// +/// Formats and signs JSON Web Tokens (JWTs) according RFC 7519 for the authentication service itself. +/// These tokens are utilized in various API endpoints for authentication and authorization purposes. +/// +public interface IAuthServiceJwtFormatter +{ + /// + /// Formats a JWT asynchronously for the authentication service itself. + /// + /// The JWT to format. + /// A formatted JWT as a string. + Task FormatAsync(JsonWebToken token); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Formatters/IClientJwtFormatter.cs b/Abblix.Oidc.Server/Features/Tokens/Formatters/IClientJwtFormatter.cs new file mode 100644 index 00000000..506b2771 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Formatters/IClientJwtFormatter.cs @@ -0,0 +1,41 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Features.Tokens.Formatters; + +/// +/// Formats JWTs (JSON Web Tokens) issued to clients of the authentication service. +/// Implements the formatting of JWT object model to a string format according RFC 7519. +/// +public interface IClientJwtFormatter +{ + /// + /// Formats a JWT for a client using the provided token and client information. + /// + /// The JWT token to format. + /// The client information. + /// The formatted JWT string. + Task FormatAsync(JsonWebToken token, ClientInfo clientInfo); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/IAccessTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IAccessTokenService.cs new file mode 100644 index 00000000..acfc4839 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/IAccessTokenService.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Defines operations related to access tokens, including their creation and authentication. +/// +public interface IAccessTokenService +{ + /// + /// Asynchronously creates a new access token based on the given authentication session, authorization context + /// and client information. + /// + /// The authentication session containing user and session details. + /// The authorization context containing details about the granted permissions and scopes. + /// Information about the client for whom the token is being created. + /// A task that represents the asynchronous create operation. + /// The task result contains the newly created . + Task CreateAccessTokenAsync(AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo); + + /// + /// Asynchronously authenticates a user based on a provided access token. + /// + /// The access token to authenticate. + /// A task that represents the asynchronous authentication operation. The task result contains + /// the and associated with the authenticated user. + Task<(AuthSession, AuthorizationContext)> AuthenticateByAccessTokenAsync(JsonWebToken accessToken); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs new file mode 100644 index 00000000..e3559a78 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs @@ -0,0 +1,70 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Provides functionality for creating and managing identity tokens as part of OpenID Connect authentication processes. +/// This service is responsible for generating identity tokens that encapsulate the authenticated user's identity +/// information and any additional claims or information authorized for release to the requesting client application. +/// +public interface IIdentityTokenService +{ + /// + /// Asynchronously generates a new identity token based on the provided authentication session, authorization + /// context, client information, and optionally includes `c_hash` or `at_hash` claims when an authorization code or + /// access token is provided. These claims are used for validating the hash of the authorization code and + /// access token respectively, enhancing the security of token exchange in OAuth 2.0 flows. + /// + /// The authentication session, which includes details such as the user's unique + /// identifier and the session's authentication time. + /// The authorization context, providing contextual information about the authorization + /// request, such as the requested scopes and additional claims to be included in the identity token. + /// Detailed information about the client that is requesting the identity token, including + /// its identifier and specific requirements for the token. + /// A boolean value indicating whether claims about the user should be included + /// in the identity token. When set to true, the token will include claims based on the requested scopes + /// and additional specified claims. + /// An optional authorization code provided to include the `c_hash` claim in the + /// identity token, which is a hash of the code. This parameter is typically used in authorization code flow. + /// + /// An optional access token provided to include the `at_hash` claim in the identity token, + /// which is a hash of the token. This parameter is used when the identity token is issued alongside an access token. + /// + /// A task that, upon completion, yields an that represents the newly + /// generated identity token, encoded and ready for transmission to the client. + /// + /// Identity tokens generated by this service adhere to the OpenID Connect standard, ensuring that they can be used + /// reliably in identity assertions across different clients and services that support OpenID Connect. + /// + Task CreateIdentityTokenAsync( + AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo, + bool includeUserClaims, + string? authorizationCode, + string? accessToken); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/ILogoutTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/ILogoutTokenService.cs new file mode 100644 index 00000000..2448cf82 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/ILogoutTokenService.cs @@ -0,0 +1,51 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.LogoutNotification; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Provides functionality for generating logout tokens as part of the OpenID Connect back-channel logout process. +/// Logout tokens are used to notify clients about the logout event of an authenticated user session. +/// +public interface ILogoutTokenService +{ + /// + /// Asynchronously generates a logout token that encapsulates information about a user's logout event. + /// This token is sent to clients to initiate the back-channel logout process, enabling them to clean up + /// user sessions in accordance with the OpenID Connect back-channel logout specification. + /// + /// Details about the client application that will receive the logout token. This includes + /// the client's identifier and other relevant configuration settings that may affect the token generation process. + /// + /// Contextual information related to the logout event, such as the user's identifier + /// (sub) and the session identifier (sid) that uniquely identifies the session being logged out. + /// Additional information about the logout event, such as the reason for logout, can also be included if supported + /// by the implementation. + /// A task that represents the asynchronous operation, resulting in a . + /// This token is specifically formatted to conform to the OpenID Connect back-channel logout specification, + /// containing claims such as 'sub', 'sid', and 'events' to indicate the logout event to the client. + Task CreateLogoutTokenAsync(ClientInfo clientInfo, LogoutContext logoutContext); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/IRefreshTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IRefreshTokenService.cs new file mode 100644 index 00000000..c64f96d0 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/IRefreshTokenService.cs @@ -0,0 +1,49 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Implements operations with refresh tokens. +/// +public interface IRefreshTokenService +{ + /// + /// Creates a new refresh token using specified parameters. + /// + Task CreateRefreshTokenAsync( + AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo, + JsonWebToken? refreshToken); + + /// + /// Authenticates a user by refresh token. + /// + public Task AuthorizeByRefreshTokenAsync(JsonWebToken refreshToken); +} diff --git a/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs new file mode 100644 index 00000000..975b458e --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs @@ -0,0 +1,185 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Text; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Features.UserInfo; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Facilitates the creation and management of identity tokens as part of the OpenID Connect authentication flow. +/// This service constructs identity tokens that encapsulate the authenticated user's identity, adhering to +/// OpenID Connect specifications. It integrates additional security by incorporating claims for token integrity +/// verification. +/// +internal class IdentityTokenService : IIdentityTokenService +{ + /// + /// Initializes a new instance of the class, setting up the necessary components + /// for identity token creation. + /// + /// Provides the issuer URL, used in the 'iss' claim of the identity token. + /// Provides the current UTC time, used to set the issued and expiration times of the identity + /// token. + /// Handles the formatting and signing of the JSON Web Token, ensuring it meets + /// the security requirements for transmission. + /// Retrieves user-specific claims to be embedded in the identity token, + /// based on the authentication session and client's requested scopes and claims. + public IdentityTokenService( + IIssuerProvider issuerProvider, + TimeProvider clock, + IClientJwtFormatter jwtFormatter, + IUserClaimsProvider userClaimsProvider) + { + _issuerProvider = issuerProvider; + _clock = clock; + _jwtFormatter = jwtFormatter; + _userClaimsProvider = userClaimsProvider; + } + + private readonly IIssuerProvider _issuerProvider; + private readonly TimeProvider _clock; + private readonly IClientJwtFormatter _jwtFormatter; + private readonly IUserClaimsProvider _userClaimsProvider; + + /// + /// Generates an identity token encapsulating the user's authenticated session, optionally embedding claims based on + /// the provided authorization code and access token. This method crafts a token that includes standard claims, + /// user-specific claims if required, and `c_hash` or `at_hash` to validate the authorization code and access token + /// integrity. + /// + /// Details of the authenticated user's session. + /// Contextual information about the authorization, including scopes and nonce. + /// Information about the requesting client. + /// Indicates whether to include detailed user claims in the token. + /// Authorization code to generate `c_hash`, validating the code's integrity. + /// + /// Access token to generate `at_hash`, ensuring the token's integrity. + /// A task that resolves to an , representing the identity token. + /// + /// + /// This implementation ensures the identity token complies with OpenID Connect specifications, facilitating secure + /// user identification across services. It explicitly handles `c_hash` and `at_hash` creation, providing additional + /// security checks for token integrity. + /// + public async Task CreateIdentityTokenAsync( + AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo, + bool includeUserClaims, + string? authorizationCode, + string? accessToken) + { + var scope = authContext.Scope; + if (!includeUserClaims && !clientInfo.ForceUserClaimsInIdentityToken) + { + // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4 + // The Claims requested by the profile, email, address, and phone scope values are returned from the UserInfo Endpoint, + // as described in Section 5.3.2, when a response_type value is used that results in an Access Token being issued. + // However, when no Access Token is issued (which is the case for the response_type value id_token), + // the resulting Claims are returned in the ID Token. + scope = scope.Except(new[] { Scopes.Profile, Scopes.Email, Scopes.Address }).ToArray(); + } + + var userInfo = await _userClaimsProvider.GetUserClaimsAsync( + authSession, + scope, + authContext.RequestedClaims?.IdToken, + clientInfo); + + if (userInfo == null) + return null; + + var issuedAt = _clock.GetUtcNow(); + + var identityToken = new JsonWebToken + { + Header = + { + Algorithm = clientInfo.IdentityTokenSignedResponseAlgorithm, + }, + Payload = new JsonWebTokenPayload(userInfo) + { + IssuedAt = issuedAt, + NotBefore = issuedAt, + ExpiresAt = issuedAt + clientInfo.IdentityTokenExpiresIn, + Issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()), + + SessionId = authSession.SessionId, + AuthenticationTime = authSession.AuthenticationTime, + [JwtClaimTypes.AuthContextClassRef] = authSession.AuthContextClassRef, + + Audiences = new[] { authContext.ClientId }, + Nonce = authContext.Nonce, + }, + }; + + AppendAdditionalClaims(identityToken, authorizationCode, accessToken); + + return new EncodedJsonWebToken(identityToken, await _jwtFormatter.FormatAsync(identityToken, clientInfo)); + } + + private static void AppendAdditionalClaims( + JsonWebToken identityToken, + string? authorizationCode, + string? accessToken) + { + Func hashFunc; + switch (identityToken.Header.Algorithm) + { + case SigningAlgorithms.RS256: + hashFunc = SHA256.HashData; + break; + + default: + return; + } + + AddHashClaim(identityToken, hashFunc, JwtClaimTypes.CodeHash, authorizationCode); + AddHashClaim(identityToken, hashFunc, JwtClaimTypes.AccessTokenHash, accessToken); + } + + private static void AddHashClaim( + JsonWebToken identityToken, + Func hashFunc, + string claimType, + string? sourceValue) + { + if (!sourceValue.HasValue()) + return; + + var hashBytes = hashFunc(Encoding.ASCII.GetBytes(sourceValue)); + var hashString = HttpServerUtility.UrlTokenEncode(hashBytes, hashBytes.Length / 2); + + identityToken.Payload[claimType] = hashString; + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs new file mode 100644 index 00000000..0743a463 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs @@ -0,0 +1,132 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.LogoutNotification; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Features.UserInfo; +using Abblix.Utils; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Implements the interface to generate logout tokens. +/// +public class LogoutTokenService : ILogoutTokenService +{ + /// + /// Initializes a new instance of the class with required dependencies for + /// token generation and formatting. + /// + /// Logger for logging operations related to logout token generation. + /// Clock used for setting token validity timestamps. + /// + /// Converter for transforming subject identifiers based on client configurations. + /// Formatter for encoding the generated logout token into a compact serialized format. + /// + public LogoutTokenService( + ILogger logger, + TimeProvider clock, + ISubjectTypeConverter subjectTypeConverter, + IClientJwtFormatter jwtFormatter) + { + _logger = logger; + _clock = clock; + _subjectTypeConverter = subjectTypeConverter; + _jwtFormatter = jwtFormatter; + } + + private readonly ILogger _logger; + private readonly TimeProvider _clock; + private readonly ISubjectTypeConverter _subjectTypeConverter; + private readonly IClientJwtFormatter _jwtFormatter; + + /// + /// Asynchronously creates a logout token based on the provided client information and logout event context. + /// The token is then encoded to a serialized string format for easy distribution to clients. + /// + /// Information about the client that will receive the logout token. + /// Contextual information about the logout event, including the user's subject ID + /// and session ID. + /// A task that represents the asynchronous operation of creating and encoding a logout token. + /// The task result is an , which includes both the raw token object and its + /// string representation. + public async Task CreateLogoutTokenAsync(ClientInfo clientInfo, LogoutContext logoutContext) + { + var logoutOptions = clientInfo.BackChannelLogout.NotNull(nameof(clientInfo.BackChannelLogout)); + if (logoutOptions.RequiresSessionId && string.IsNullOrEmpty(logoutContext.SessionId)) + { + throw new InvalidOperationException($"The client {clientInfo.ClientId} requires session id"); + } + + var subjectId = _subjectTypeConverter.Convert(logoutContext.SubjectId, clientInfo); + if (string.IsNullOrEmpty(subjectId) && string.IsNullOrEmpty(logoutContext.SessionId)) + { + throw new InvalidOperationException( + $"Both {nameof(subjectId)} and {nameof(logoutContext.SessionId)} are null or empty, unable to specify the session should be finished"); + } + + //TODO extract id generator to separate class + var jwtId = CryptoRandom.GetRandomBytes(16).ToHexString(); + + var issuedAt = _clock.GetUtcNow(); + + var logoutToken = new JsonWebToken + { + Header = + { + Type = JwtTypes.LogoutToken, + Algorithm = SigningAlgorithms.RS256, + }, + Payload = + { + // Attention: according to the https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken + // the nonce is PROHIBITED in Logout tokens. + + JwtId = jwtId, + + IssuedAt = issuedAt, + NotBefore = issuedAt, + ExpiresAt = issuedAt + logoutOptions.LogoutTokenExpiresIn, + + Issuer = logoutContext.Issuer, + Audiences = new[] { clientInfo.ClientId }, + + Subject = subjectId, + SessionId = logoutContext.SessionId, + + [JwtClaimTypes.Events] = new JsonObject + { + { "http://schemas.openid.net/event/backchannel-logout", new JsonObject() }, + } + }, + }; + + _logger.LogDebug("The logout token was prepared {@LogoutToken}", logoutToken); + + return new EncodedJsonWebToken(logoutToken, await _jwtFormatter.FormatAsync(logoutToken, clientInfo)); + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/RefreshTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/RefreshTokenService.cs new file mode 100644 index 00000000..309ec3e8 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/RefreshTokenService.cs @@ -0,0 +1,154 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common; +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Endpoints.Token.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; +using Abblix.Oidc.Server.Features.RandomGenerators; +using Abblix.Oidc.Server.Features.Storages; +using Abblix.Oidc.Server.Features.Tokens.Formatters; +using Abblix.Oidc.Server.Features.Tokens.Revocation; +using Abblix.Oidc.Server.Features.UserAuthentication; + +namespace Abblix.Oidc.Server.Features.Tokens; + +/// +/// Manages refresh tokens, key components in OAuth 2.0 for extending authentication sessions without requiring +/// user re-authentication. This service handles the creation and validation of refresh tokens, supporting seamless +/// and secure user experiences by allowing access tokens to be renewed based on long-lived refresh tokens. +/// +public class RefreshTokenService : IRefreshTokenService +{ + public RefreshTokenService( + IIssuerProvider issuerProvider, + TimeProvider clock, + ITokenIdGenerator tokenIdGenerator, + IAuthServiceJwtFormatter jwtFormatter, + ITokenRegistry tokenRegistry) + { + _issuerProvider = issuerProvider; + _clock = clock; + _tokenIdGenerator = tokenIdGenerator; + _jwtFormatter = jwtFormatter; + _tokenRegistry = tokenRegistry; + } + + private readonly IIssuerProvider _issuerProvider; + private readonly TimeProvider _clock; + private readonly ITokenIdGenerator _tokenIdGenerator; + private readonly IAuthServiceJwtFormatter _jwtFormatter; + private readonly ITokenRegistry _tokenRegistry; + + /// + /// Generates a new refresh token based on the user's current authentication session and authorization context, + /// optionally renewing an existing refresh token. This facilitates prolonged access without re-authentication, + /// adhering to specified client policies for token expiration and renewal. + /// + /// The session details of the authenticated user, providing context for token issuance. + /// + /// Contextual information from the authorization process, including scopes and + /// client-specific settings. + /// Details of the client application requesting the token, used to apply appropriate + /// token policies. + /// An existing refresh token to be renewed, if applicable. A new token is created + /// if this is null or expired. + /// A task that results in a new or renewed representing + /// the refresh token, or null if the existing token cannot be renewed due to policy constraints or expiration. + /// + public async Task CreateRefreshTokenAsync( + AuthSession authSession, + AuthorizationContext authContext, + ClientInfo clientInfo, + JsonWebToken? refreshToken) + { + if (!clientInfo.RefreshToken.AllowReuse && + refreshToken is { Payload: { JwtId: { } jwtId, ExpiresAt: {} expiresAt }}) + { + // Revokes used refresh token to prevent its reuse + await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Revoked, expiresAt); + } + + var now = _clock.GetUtcNow(); + var issuedAt = refreshToken?.Payload.IssuedAt ?? now; + expiresAt = CalculateExpiresAt(issuedAt, clientInfo.RefreshToken); + if (expiresAt < now) + return null; + + var newToken = new JsonWebToken + { + Header = + { + Type = JwtTypes.RefreshToken, + Algorithm = SigningAlgorithms.RS256, + }, + Payload = + { + JwtId = _tokenIdGenerator.GenerateTokenId(), + IssuedAt = issuedAt, + NotBefore = now, + ExpiresAt = expiresAt, + Issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()), + Audiences = new[] { clientInfo.ClientId }, + }, + }; + authSession.ApplyTo(newToken.Payload); + authContext.ApplyTo(newToken.Payload); + + return new EncodedJsonWebToken(newToken, await _jwtFormatter.FormatAsync(newToken)); + } + + private static DateTimeOffset CalculateExpiresAt(DateTimeOffset issuedAt, RefreshTokenOptions options) + { + var expiresAt = issuedAt + options.AbsoluteExpiresIn; + + if (options.SlidingExpiresIn.HasValue) + { + var sliding = issuedAt + options.SlidingExpiresIn.Value; + if (sliding < expiresAt) + return sliding; + } + + return expiresAt; + } + + /// + /// Validates and authorizes a provided refresh token, reconstructing the user's authentication session and + /// authorization context. This method facilitates continued access by validating the refresh token's integrity + /// and expiry, granting a new access token for continued use. + /// + /// The refresh token to be validated and authorized. + /// A task that, upon successful validation, results in an + /// encapsulating the reconstituted authentication session and authorization context. + public Task AuthorizeByRefreshTokenAsync(JsonWebToken refreshToken) + { + var authSession = refreshToken.Payload.ToAuthSession(); + var authContext = refreshToken.Payload.ToAuthorizationContext(); + var result = new RefreshTokenAuthorizedGrant(authSession, authContext, refreshToken); + + return Task.FromResult(result); + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Revocation/JsonWebTokenStatus.cs b/Abblix.Oidc.Server/Features/Tokens/Revocation/JsonWebTokenStatus.cs new file mode 100644 index 00000000..46c9a9d3 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Revocation/JsonWebTokenStatus.cs @@ -0,0 +1,48 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.Tokens.Revocation; + +/// +/// Defines the possible states of a JSON Web Token within the system. +/// +public enum JsonWebTokenStatus +{ + /// + /// Indicates that the status of the token is not known. + /// This may be used as a default value when the token's status has not been explicitly set or determined. + /// + Unknown, + + /// + /// Indicates that the token has been used. + /// This status can be used to mark tokens that have been consumed in a process, such as authorization codes that have been exchanged for access tokens. + /// + Used, + + /// + /// Indicates that the token has been revoked. + /// A revoked token is no longer valid for use and should be rejected in any validation checks. + /// This status is typically set when a user or system administrator manually invalidates a token. + /// + Revoked, +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Revocation/TokenStatusValidatorDecorator.cs b/Abblix.Oidc.Server/Features/Tokens/Revocation/TokenStatusValidatorDecorator.cs new file mode 100644 index 00000000..14f7fe4d --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Revocation/TokenStatusValidatorDecorator.cs @@ -0,0 +1,79 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Features.Storages; + +namespace Abblix.Oidc.Server.Features.Tokens.Revocation; + +/// +/// Enhances the functionality of an existing by adding token revocation validation capabilities. +/// This decorator checks whether the JSON Web Token (JWT) has been revoked or used before and, if so, invalidates the token. +/// It utilizes an to check the token's status and an inner +/// for initial token validation. +/// +public class TokenStatusValidatorDecorator : IJsonWebTokenValidator +{ + public TokenStatusValidatorDecorator( + ITokenRegistry tokenRegistry, + IJsonWebTokenValidator innerValidator) + { + _tokenRegistry = tokenRegistry; + _innerValidator = innerValidator; + } + + private readonly ITokenRegistry _tokenRegistry; + private readonly IJsonWebTokenValidator _innerValidator; + + public IEnumerable SigningAlgValuesSupported => _innerValidator.SigningAlgValuesSupported; + + /// + /// Validates a JSON Web Token (JWT) and checks its revocation status. + /// + /// The JWT to be validated. + /// Validation parameters to use during validation. + /// + /// A indicating the validation outcome. + /// If the token is revoked or already used, it returns a . + /// Otherwise, it returns the result from the inner validator. + /// + public async Task ValidateAsync( + string jwt, + ValidationParameters parameters) + { + var result = await _innerValidator.ValidateAsync(jwt, parameters); + + if (result is ValidJsonWebToken { Token.Payload.JwtId: { } jwtId }) + { + switch (await _tokenRegistry.GetStatusAsync(jwtId)) + { + case JsonWebTokenStatus.Used: + return new JwtValidationError(JwtError.TokenAlreadyUsed, "Token was already used"); + + case JsonWebTokenStatus.Revoked: + return new JwtValidationError(JwtError.TokenRevoked, "Token was revoked"); + } + } + + return result; + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Validation/AuthServiceJwtValidator.cs b/Abblix.Oidc.Server/Features/Tokens/Validation/AuthServiceJwtValidator.cs new file mode 100644 index 00000000..75b7fe92 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Validation/AuthServiceJwtValidator.cs @@ -0,0 +1,97 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Interfaces; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.Issuer; +using Abblix.Oidc.Server.Features.Licensing; + +namespace Abblix.Oidc.Server.Features.Tokens.Validation; + +/// +/// Validates JWTs (JSON Web Tokens) used in the authentication service. +/// Ensures tokens meet the necessary criteria for issuer, audience and signing keys, +/// as defined in the OAuth 2.0 and OpenID Connect standards. +/// +public class AuthServiceJwtValidator : IAuthServiceJwtValidator +{ + public AuthServiceJwtValidator( + IJsonWebTokenValidator validator, + IClientInfoProvider clientInfoProvider, + IIssuerProvider issuerProvider, + IAuthServiceKeysProvider serviceKeysProvider) + { + _validator = validator; + _clientInfoProvider = clientInfoProvider; + _issuerProvider = issuerProvider; + _serviceKeysProvider = serviceKeysProvider; + } + + private readonly IJsonWebTokenValidator _validator; + private readonly IClientInfoProvider _clientInfoProvider; + private readonly IIssuerProvider _issuerProvider; + private readonly IAuthServiceKeysProvider _serviceKeysProvider; + + /// + /// Validates a JWT for authenticity and compliance with the expected issuer, audience, and cryptographic signatures. + /// + /// The JWT to validate. + /// Validation options to apply. Default is . + /// A task representing the asynchronous validation operation, which upon completion yields a . + public Task ValidateAsync(string jwt, ValidationOptions options = ValidationOptions.Default) + { + return _validator.ValidateAsync( + jwt, + new ValidationParameters + { + Options = options, + ValidateIssuer = ValidateIssuerAsync, + ValidateAudience = ValidateAudienceAsync, + ResolveIssuerSigningKeys = _ => _serviceKeysProvider.GetSigningKeys(), + ResolveTokenDecryptionKeys = _ => _serviceKeysProvider.GetEncryptionKeys(true), + }); + } + + private Task ValidateIssuerAsync(string issuer) + { + var result = issuer == _issuerProvider.GetIssuer(); + if (result) + { + LicenseChecker.CheckIssuer(issuer); + } + + return Task.FromResult(result); + } + + private async Task ValidateAudienceAsync(IEnumerable audiences) + { + foreach (var audience in audiences) + { + var clientInfo = await _clientInfoProvider.TryFindClientAsync(audience).WithLicenseCheck(); + if (clientInfo != null) + return true; + } + + return false; + } +} diff --git a/Abblix.Oidc.Server/Features/Tokens/Validation/IAuthServiceJwtValidator.cs b/Abblix.Oidc.Server/Features/Tokens/Validation/IAuthServiceJwtValidator.cs new file mode 100644 index 00000000..ba60ecb4 --- /dev/null +++ b/Abblix.Oidc.Server/Features/Tokens/Validation/IAuthServiceJwtValidator.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; + +namespace Abblix.Oidc.Server.Features.Tokens.Validation; + +/// +/// Validates JWT issued by the OpenID service using specified options. +/// +/// +/// Returns parsed object model of JWT in case of success or detailed error otherwise. +/// +public interface IAuthServiceJwtValidator +{ + public Task ValidateAsync(string jwt, ValidationOptions options = ValidationOptions.Default); +} diff --git a/Abblix.Oidc.Server/Features/UriValidation/CompositeUriValidator.cs b/Abblix.Oidc.Server/Features/UriValidation/CompositeUriValidator.cs new file mode 100644 index 00000000..e5264c2c --- /dev/null +++ b/Abblix.Oidc.Server/Features/UriValidation/CompositeUriValidator.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UriValidation; + +/// +/// Represents a composite URI validator that combines multiple URI validation strategies. +/// This class allows the integration of various URI validation rules and abstracts the complexity of individual +/// validations, providing a unified interface for URI validation. +/// +/// +/// The CompositeUriValidator class follows the Composite Design Pattern, enabling the combination of different +/// URI validation strategies into a single cohesive unit. This class is particularly useful in scenarios where +/// URIs need to be validated against multiple criteria before being deemed valid. +/// +public sealed class CompositeUriValidator : IUriValidator +{ + public CompositeUriValidator(IEnumerable validators) + { + _validators = validators; + } + + public CompositeUriValidator(params IUriValidator[] validators) + : this((IEnumerable)validators) + { + } + + private readonly IEnumerable _validators; + + /// + /// Determines whether the specified URI is valid based on the criteria of all validators in the composite. + /// + /// The URI to validate. + /// + /// true if the URI passes the validation rules of all included validators; otherwise, false. + /// + public bool IsValid(Uri uri) => _validators.Any(validator => validator.IsValid(uri)); +} diff --git a/Abblix.Oidc.Server/Features/UriValidation/ExactMatchUriValidator.cs b/Abblix.Oidc.Server/Features/UriValidation/ExactMatchUriValidator.cs new file mode 100644 index 00000000..0895ce12 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UriValidation/ExactMatchUriValidator.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.UriValidation; + +/// +/// Validates a URI based on an exact match against a predefined value, disregarding the query and fragment parts. +/// +public sealed class ExactMatchUriValidator : IUriValidator +{ + public ExactMatchUriValidator(Uri validUri, bool ignoreQueryAndFragment = false) + { + if (!validUri.IsAbsoluteUri) + { + throw new ArgumentException($"{nameof(validUri)} must be absolute"); + } + _validUri = validUri; + _ignoreQueryAndFragment = ignoreQueryAndFragment; + } + + private readonly Uri _validUri; + private readonly bool _ignoreQueryAndFragment; + + /// + /// Validates the specified URI by checking for an exact match with the predefined URI. + /// + /// The URI to validate. + /// true if the specified URI exactly matches the predefined URI, otherwise false. + public bool IsValid(Uri uri) + { + if (_ignoreQueryAndFragment && (uri.Query.HasValue() || uri.Fragment.HasValue())) + { + uri = new System.UriBuilder(uri) { Query = null, Fragment = null }.Uri; + } + + return _validUri == uri; + } +} diff --git a/Abblix.Oidc.Server/Features/UriValidation/IUriValidator.cs b/Abblix.Oidc.Server/Features/UriValidation/IUriValidator.cs new file mode 100644 index 00000000..9745f7b5 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UriValidation/IUriValidator.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UriValidation; + +/// +/// Defines a contract for URI validation. +/// +public interface IUriValidator +{ + /// + /// Validates the specified URI against predefined rules. + /// + /// The URI to validate. + /// true if the URI meets the validation criteria; otherwise, false. + bool IsValid(Uri uri); +} diff --git a/Abblix.Oidc.Server/Features/UriValidation/UriValidatorFactory.cs b/Abblix.Oidc.Server/Features/UriValidation/UriValidatorFactory.cs new file mode 100644 index 00000000..053d7359 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UriValidation/UriValidatorFactory.cs @@ -0,0 +1,47 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UriValidation; + +/// +/// Provides a factory method for creating URI validators. Depending on the number of URIs provided, +/// it creates either a single exact match validator or a composite validator that combines multiple exact match validators. +/// +public static class UriValidatorFactory +{ + /// + /// Creates a URI validator based on the given URIs. + /// If only one URI is provided, it returns an for that URI. + /// If multiple URIs are provided, it returns a that validates against all given URIs. + /// + /// An array of URIs to be used for validation. + /// + /// An instance that can validate URIs based on the provided URIs. + /// Returns an for a single URI or a for multiple URIs. + /// + public static IUriValidator Create(params Uri[] validUris) + => validUris.Length switch + { + 1 => new ExactMatchUriValidator(validUris[0]), + _ => new CompositeUriValidator(from validUri in validUris select new ExactMatchUriValidator(validUri)), + }; +} diff --git a/Abblix.Oidc.Server/Features/UserAuthentication/AuthSession.cs b/Abblix.Oidc.Server/Features/UserAuthentication/AuthSession.cs new file mode 100644 index 00000000..e85e1ddc --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserAuthentication/AuthSession.cs @@ -0,0 +1,68 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UserAuthentication; + +/// +/// Represents a model of an authentication session for a logged-in user, capturing essential details about the user's +/// authentication state and interactions within the system. +/// +public record AuthSession(string Subject, string SessionId, DateTimeOffset AuthenticationTime, string IdentityProvider) +{ + /// + /// The unique identifier for the user in the session. This is typically a user-specific identifier that can be + /// used to retrieve user details or verify the user's identity across different parts of the application. + /// + public string Subject { get; init; } = Subject; + + /// + /// The unique identifier of the session, used to track the session across requests and possibly across + /// different services. + /// + public string SessionId { get; init; } = SessionId; + + /// + /// The timestamp indicating when the user was authenticated. This is used for session management purposes such as + /// session expiration and activity logging. + /// + public DateTimeOffset AuthenticationTime { get; init; } = AuthenticationTime; + + /// + /// The provider used to authenticate the user's identity. This could be a local database, an external identity + /// provider, or a social login provider, and can be useful for auditing and enforcing security policies based + /// on the origin of authentication. + /// + public string? IdentityProvider { get; init; } = IdentityProvider; + + /// + /// Indicates the authentication context class that the authentication performed satisfied, according to + /// specifications such as SAML 2.0 or OpenID Connect. This may dictate the level of assurance provided + /// by the authentication process. + /// + public string? AuthContextClassRef { get; init; } + + /// + /// A collection of client identifiers that the user has interacted with during the session. + /// This can be used to manage and track user consent and interaction with multiple clients within the same session. + /// + public ICollection AffectedClientIds { get; init; } = new List(); +} diff --git a/Abblix.Oidc.Server/Features/UserAuthentication/AuthSessionExtensions.cs b/Abblix.Oidc.Server/Features/UserAuthentication/AuthSessionExtensions.cs new file mode 100644 index 00000000..b432148a --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserAuthentication/AuthSessionExtensions.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.UserAuthentication; + +/// +/// Provides extension methods for , facilitating the conversion +/// between authentication session information and JWT claims. +/// +public static class AuthSessionExtensions +{ + /// + /// Applies the values from an object to a , + /// effectively transferring the authentication session information into JWT claims. + /// + /// The authentication session containing the user's authentication information. + /// The JWT payload where the authentication session information will be applied as claims. + /// + /// This method is useful for including specific authentication session details, such as the subject, + /// authentication time, and session ID, into the JWT for subsequent validation or processing. + /// + public static void ApplyTo(this AuthSession authSession, JsonWebTokenPayload payload) + { + payload.Subject = authSession.Subject; + payload.IdentityProvider = authSession.IdentityProvider; + payload.SessionId = authSession.SessionId; + payload.AuthenticationTime = authSession.AuthenticationTime; + } + + /// + /// Creates an object from a collection of JWT claims present in a . + /// + /// The JWT payload containing claims that represent an authentication session. + /// An instance of populated with information derived from the JWT claims. + /// + /// This method allows for the reconstruction of an authentication session from the claims encoded in a JWT. + /// It is particularly useful when processing JWTs to extract authentication and user session details. + /// + public static AuthSession ToAuthSession(this JsonWebTokenPayload payload) => new( + payload.Subject.NotNull(nameof(payload.Subject)), + payload.SessionId.NotNull(nameof(payload.SessionId)), + payload.AuthenticationTime.NotNull(nameof(payload.AuthenticationTime)), + payload.IdentityProvider.NotNull(nameof(payload.IdentityProvider))); +} diff --git a/Abblix.Oidc.Server/Features/UserAuthentication/IAuthSessionService.cs b/Abblix.Oidc.Server/Features/UserAuthentication/IAuthSessionService.cs new file mode 100644 index 00000000..64e7317b --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserAuthentication/IAuthSessionService.cs @@ -0,0 +1,62 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UserAuthentication; + +/// +/// Manages user authentication, providing mechanisms to sign in, sign out, and maintain user sessions. +/// This interface plays a pivotal role in the security and session management of an application, ensuring that users are +/// authenticated and their sessions are managed securely across different contexts and client applications. +/// +public interface IAuthSessionService +{ + /// + /// Retrieves currently active authentication sessions for the user. + /// This method is typically used to display all sessions that a user has, allowing them to manage their sessions. + /// + /// An asynchronous stream of instances, each representing an active user session. + IAsyncEnumerable GetAvailableAuthSessions(); + + /// + /// Authenticates the current user based on the session context, verifying their identity and session validity. + /// This method is crucial for ensuring that requests are made by an authenticated user and for retrieving the user's session information. + /// + /// + /// A task that resolves to an representing the authenticated user's session, or null if no valid session exists. + /// + Task AuthenticateAsync(); + + /// + /// Initiates a new user session based on provided user claims, effectively signing in the user. + /// This method is essential for establishing new user sessions following successful authentication. + /// + /// Detailed information about the authentication session to be established. + /// A task that signifies the completion of the user sign-in process. + Task SignInAsync(AuthSession authSession); + + /// + /// Terminates the current user session, effectively signing out the user. + /// This method is crucial for maintaining the security of the application by ensuring that user sessions can be properly closed. + /// + /// A task that signifies the completion of the user sign-out process. + Task SignOutAsync(); +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs new file mode 100644 index 00000000..aca66598 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Defines a service responsible for determining the claims associated with specific OAuth 2.0 and OpenID Connect scopes. +/// This interface facilitates the mapping of requested scopes to their corresponding claims, enabling effective claims +/// management based on the authorization policies and client request parameters. +/// +public interface IScopeClaimsProvider +{ + /// + /// Retrieves the set of claim names associated with the requested scopes and any additional claim details. + /// This method allows for dynamic claim resolution based on the authorization request, supporting customization + /// of claims returned in tokens or user info responses. + /// + /// An enumerable of strings representing the requested scopes. Each scope can be associated + /// with one or multiple claims as defined by the implementation. + /// An optional collection of additional claims requested, which may not necessarily + /// be tied to specific scopes but are required by the client. + /// An IEnumerable of strings, each representing a claim name that should be included based on the + /// requested scopes and additional claims. + IEnumerable GetRequestedClaims(IEnumerable scopes, IEnumerable? requestedClaims); + + /// + /// Provides a collection of all the scopes that are recognized and supported by this provider. + /// This property can be used to validate scope requests or to generate metadata for discovery documents. + /// + IEnumerable ScopesSupported { get; } + + /// + /// Provides a collection of all the claims that this provider can handle. + /// These claims represent the total set of data points that can be requested through various scopes + /// and are used for constructing tokens and user information responses. + /// + IEnumerable ClaimsSupported { get; } +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs b/Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs new file mode 100644 index 00000000..ebc478c6 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Features.ClientInformation; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Defines the interface for a service that converts user subject identifiers according to the client's specified +/// subject type. This conversion ensures that the subject identifier presented to the client is in the format +/// that the client expects, based on its configuration. +/// +/// +/// The implementation of this interface should support various subject types, such as "public" or "pairwise", +/// and provide appropriate conversions based on the OpenID Connect specifications. +/// +public interface ISubjectTypeConverter +{ + /// + /// Lists the subject types that this converter supports. This typically includes "public" and "pairwise" + /// among others, depending on the OpenID Connect implementation specifics. + /// + IEnumerable SubjectTypesSupported { get; } + + /// + /// Converts the subject identifier for an end-user based on the client's subject type. + /// + /// The original subject identifier of the end-user. + /// Information about the client for which the subject identifier is being transformed. + /// + /// The transformed subject identifier suitable for the client's subject type. + string Convert(string subject, ClientInfo clientInfo); +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs new file mode 100644 index 00000000..30740576 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs @@ -0,0 +1,60 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Defines an interface for retrieving user-specific claims based on authentication sessions and requested claims. +/// This interface plays a crucial role in authentication flows, where it extracts and formats user data for inclusion +/// in tokens or other authorization responses, ensuring compliance with specified scopes and claim requests. +/// +public interface IUserClaimsProvider +{ + /// + /// Asynchronously retrieves structured user claims based on the provided authentication session, requested scopes, + /// additional claim details, and client information. This method is crucial for generating claims that are to be + /// embedded in identity tokens or provided through user info endpoints, allowing for a personalized and secure user + /// experience based on the authenticated session and application requirements. + /// + /// The authentication session which includes details about the user's authentication + /// state and may affect the resultant claims. + /// A collection of scopes indicating which categories of claims are requested. + /// Each scope can correlate to multiple claims, influencing the granularity and type of data returned. + /// Additional details about specific claims requested, often providing finer control + /// over the claims’ properties such as essentiality or value requirements, enhancing the flexibility and + /// adaptiveness of claim retrieval. + /// Information about the client application making the request, which may influence + /// the processing and filtering of claims based on client-specific settings or requirements. + /// A task that resolves to a encapsulating the user claims in a structured JSON + /// format suitable for further processing, or null if the necessary claims cannot be retrieved or are not + /// applicable based on the session details. + Task GetUserClaimsAsync( + AuthSession authSession, + ICollection scope, + ICollection>? requestedClaims, + ClientInfo clientInfo); +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs new file mode 100644 index 00000000..bec7cda7 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs @@ -0,0 +1,66 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Provides functionality to retrieve user information as JWT claims, supporting both simple and structured claim +/// values. +/// This interface enables the dynamic extraction and packaging of user attributes into JWT claims, accommodating a +/// variety +/// of claim types including those that require complex, structured data beyond traditional scalar values. +/// +public interface IUserInfoProvider +{ + /// + /// Asynchronously retrieves a set of user claims for a specified subject identifier, including both simple and + /// structured claim values as requested by the client application. This method supports the OpenID Connect + /// specification by allowing for the selective disclosure of user information, catering to the need for complex + /// data structures within claims. + /// + /// + /// The unique subject identifier (sub claim) of the user whose information is being requested. + /// This identifier must uniquely identify the user across all applications and services. + /// + /// + /// A collection of names representing the claims requested by a client application. + /// Implementations should check against this list to return only those claims that are requested and authorized + /// for release, including both scalar values and structured data as necessary. + /// + /// + /// A task that resolves to a , encapsulating the user's claims where each entry consists of + /// a claim name and its value. The value can be a simple scalar value (e.g., a string or number) or a structured + /// object, allowing for complex data types to be represented. Returns null if no information is available for the + /// given subject. The use of facilitates the representation of hierarchical data within + /// claims, + /// supporting richer and more detailed user profiles. + /// + /// + /// Implementers should ensure that the disclosure of user information complies with applicable privacy laws and + /// the principles of data minimization. Sensitive or personal information must only be shared with explicit user + /// consent and in a secure manner. In cases where the requested user or claims are not found, returning null or an + /// empty helps maintain privacy and security. + /// + Task GetUserInfoAsync(string subject, IEnumerable requestedClaims); +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs new file mode 100644 index 00000000..36eb784c --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs @@ -0,0 +1,80 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Implements the interface to provide claim names based on requested scopes +/// and claims. This class manages the association between scopes and the specific claims they include, +/// facilitating the retrieval of appropriate claims for given scopes during the authorization process. +/// +public class ScopeClaimsProvider : IScopeClaimsProvider +{ + /// + /// A mapping from scopes to the respective arrays of claim types that each scope encompasses. + /// + private readonly Dictionary _scopeToClaimsMap = new[] + { + StandardScopes.OpenId, + StandardScopes.Profile, + StandardScopes.Email, + StandardScopes.Address, + StandardScopes.Phone, + StandardScopes.OfflineAccess, + }.ToDictionary(definition => definition.Scope, definition => definition.ClaimTypes, StringComparer.OrdinalIgnoreCase); + + /// + /// Retrieves the specific claims associated with the requested scopes and any additional requested claims. + /// + /// The collection of scopes for which claims need to be provided. + /// Additional specific claims requested outside of the scope requests. + /// A collection of claim names that are associated with the requested scopes and additional claims. + /// + public IEnumerable GetRequestedClaims( + IEnumerable scopes, + IEnumerable? requestedClaims) + { + var claimNames = scopes + .SelectMany(scope => _scopeToClaimsMap.TryGetValue(scope, out var claims) ? claims : Array.Empty()) + .Prepend(JwtClaimTypes.Subject); + + if (requestedClaims != null) + { + claimNames = claimNames.Concat(requestedClaims); + } + + return claimNames; + } + + /// + /// A collection of all the scopes supported by this provider. + /// + public IEnumerable ScopesSupported => _scopeToClaimsMap.Keys; + + /// + /// A collection of all the claims that can be provided by this provider. + /// + public IEnumerable ClaimsSupported => _scopeToClaimsMap.Values.SelectMany(claims => claims); +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs b/Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs new file mode 100644 index 00000000..0d223e34 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs @@ -0,0 +1,81 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; +using System.Text; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Utils; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Implements conversion of subject identifiers for end-users based on the subject type requested by the client. +/// +/// +/// Subject identifiers can be either directly provided to the client (public) or transformed into a pairwise +/// identifier to ensure user privacy across different clients. This implementation supports both "public" and +/// "pairwise" subject types as defined by the OpenID Connect specification. +/// +public class SubjectTypeConverter : ISubjectTypeConverter +{ + /// + /// The subject types supported by this converter. + /// + public IEnumerable SubjectTypesSupported + { + get + { + yield return SubjectTypes.Public; + yield return SubjectTypes.Pairwise; + } + } + + /// + /// Converts the subject identifier based on the client's subject type. + /// For "pairwise" subject type clients, it generates a unique and stable subject identifier by hashing + /// the original subject identifier with the client's sector identifier or client ID. + /// For "public" subject type clients, it returns the original subject identifier. + /// + /// The original subject identifier of the end-user. + /// Information about the client for which the subject identifier is being transformed, + /// including the subject type and sector identifier if applicable. + /// The transformed subject identifier for the end-user based on the client's subject type. + /// + /// This method ensures that end-users are represented differently to different clients + /// (when using "pairwise" subject type) to enhance user privacy, or consistently represented to all clients + /// (when using "public" subject type) based on the client's configuration. + /// + public string Convert(string subject, ClientInfo clientInfo) + { + switch (clientInfo.SubjectType) + { + case SubjectTypes.Pairwise: + var subjectBytes = Encoding.UTF8.GetBytes(subject + (clientInfo.SectorIdentifier ?? clientInfo.ClientId)); + var hashData = SHA512.HashData(subjectBytes); + return HttpServerUtility.UrlTokenEncode(hashData); + + default: + return subject; + } + } +} diff --git a/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs new file mode 100644 index 00000000..d618b769 --- /dev/null +++ b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs @@ -0,0 +1,127 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Nodes; +using Abblix.Jwt; +using Abblix.Oidc.Server.Features.ClientInformation; +using Abblix.Oidc.Server.Features.UserAuthentication; +using Abblix.Oidc.Server.Model; +using Microsoft.Extensions.Logging; + +namespace Abblix.Oidc.Server.Features.UserInfo; + +/// +/// Handles the retrieval of user claims for authentication sessions, ensuring compliance with requested scopes and +/// specific claim details. This class integrates directly with user information providers and scope-to-claim mappings +/// to fetch and validate the necessary user data. It supports converting user data into claims that adhere to +/// OpenID Connect standards, tailored to the specific needs of the client making the request. +/// +public class UserClaimsProvider : IUserClaimsProvider +{ + /// + /// Initializes a new instance of the class. + /// + /// The logger used for logging information and errors. + /// The provider used to retrieve detailed user information based on specific claims. + /// + /// The provider that maps requested scopes to the corresponding set of claims. + /// + /// The converter used to translate user identifiers into subject types as + /// required by different client configurations. + public UserClaimsProvider( + ILogger logger, + IUserInfoProvider userInfoProvider, + IScopeClaimsProvider scopeClaimsProvider, + ISubjectTypeConverter subjectTypeConverter) + { + _logger = logger; + _userInfoProvider = userInfoProvider; + _scopeClaimsProvider = scopeClaimsProvider; + _subjectTypeConverter = subjectTypeConverter; + } + + private readonly ILogger _logger; + private readonly IScopeClaimsProvider _scopeClaimsProvider; + private readonly IUserInfoProvider _userInfoProvider; + private readonly ISubjectTypeConverter _subjectTypeConverter; + + /// + /// Asynchronously retrieves structured user claims based on an authentication session and specific claim parameters. + /// This method ensures compliance with the OpenID Connect standards by validating essential claims and formatting + /// the user data into a structured JSON object. + /// + /// The authentication session providing the context for user claims retrieval. + /// A collection of scopes defining the categories of claims required. + /// A collection detailing specific claims requested by the client, including any + /// requirements for essential claims. + /// Information about the client application making the request, which may influence how + /// claims are processed and returned. + /// A task that when completed returns a representing the user claims, + /// or throws an exception if required claims are missing. + public async Task GetUserClaimsAsync( + AuthSession authSession, + ICollection scope, + ICollection>? requestedClaims, + ClientInfo clientInfo) + { + var claimNames = _scopeClaimsProvider.GetRequestedClaims( + scope, requestedClaims?.Select(claim => claim.Key)); + + var userInfo = await _userInfoProvider.GetUserInfoAsync( + authSession.Subject, + claimNames.Distinct(StringComparer.Ordinal)); + if (userInfo == null) + { + _logger.LogWarning("The user claims were not found by subject value"); + return null; + } + + var subject = _subjectTypeConverter.Convert(authSession.Subject, clientInfo); + userInfo.SetProperty(JwtClaimTypes.Subject, subject); + + if (FindMissingClaims(userInfo, requestedClaims) is { Length: > 0 } missingClaims) + { + _logger.LogWarning("The following claims are requested, but not returned from {IUserInfoProvider}: {@MissingClaims}", + _userInfoProvider.GetType().FullName, + missingClaims); + + return null; + } + + return userInfo; + } + + private static string[]? FindMissingClaims( + JsonObject userInfo, + ICollection>? requestedClaims) + { + if (requestedClaims == null) + return null; + + var missingClaims = ( + from claim in requestedClaims + where claim.Value.Essential == true && !userInfo.TryGetPropertyValue(claim.Key, out _) + select claim.Key).ToArray(); + + return missingClaims; + } +} diff --git a/Abblix.Oidc.Server/GlobalUsings.cs b/Abblix.Oidc.Server/GlobalUsings.cs new file mode 100644 index 00000000..e0322065 --- /dev/null +++ b/Abblix.Oidc.Server/GlobalUsings.cs @@ -0,0 +1,24 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +global using UriBuilder = Abblix.Utils.UriBuilder; +global using AllowedValuesAttribute = Abblix.Oidc.Server.DeclarativeValidation.AllowedValuesAttribute; diff --git a/Abblix.Oidc.Server/Model/Address.cs b/Abblix.Oidc.Server/Model/Address.cs new file mode 100644 index 00000000..f832d527 --- /dev/null +++ b/Abblix.Oidc.Server/Model/Address.cs @@ -0,0 +1,90 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a physical address, providing various components typically used in postal addresses. +/// +public record Address +{ + private static class Parameters + { + public const string Formatted = "formatted"; + public const string StreetAddress = "street_address"; + public const string Locality = "locality"; + public const string Region = "region"; + public const string PostalCode = "postal_code"; + public const string Country = "country"; + } + + /// + /// Full mailing address, formatted for display or use on a mailing label. + /// This field MAY contain multiple lines, separated by newlines. + /// Newlines can be represented either as a carriage return/line feed pair ("\r\n") + /// or as a single line feed character ("\n"). + /// + [JsonPropertyName(Parameters.Formatted)] + [JsonPropertyOrder(1)] + public string? Formatted { get; set; } + + /// + /// Full street address component, which MAY include house number, street name, Post Office Box, + /// and multi-line extended street address information. + /// This field MAY contain multiple lines, separated by newlines. + /// Newlines can be represented either as a carriage return/line feed pair ("\r\n") + /// or as a single line feed character ("\n"). + /// + [JsonPropertyName(Parameters.StreetAddress)] + [JsonPropertyOrder(2)] + public string? StreetAddress { get; set; } + + /// + /// City or locality component. + /// + [JsonPropertyName(Parameters.Locality)] + [JsonPropertyOrder(3)] + public string? Locality { get; set; } + + /// + /// State, province, prefecture, or region component. + /// + [JsonPropertyName(Parameters.Region)] + [JsonPropertyOrder(4)] + public string? Region { get; set; } + + /// + /// Zip code or postal code component. + /// + [JsonPropertyName(Parameters.PostalCode)] + [JsonPropertyOrder(5)] + public string? PostalCode { get; set; } + + /// + /// Country name component. + /// + [JsonPropertyName(Parameters.Country)] + [JsonPropertyOrder(6)] + public string? Country { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/AuthorizationRequest.cs b/Abblix.Oidc.Server/Model/AuthorizationRequest.cs new file mode 100644 index 00000000..dcb498ab --- /dev/null +++ b/Abblix.Oidc.Server/Model/AuthorizationRequest.cs @@ -0,0 +1,209 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using System.Text.Json.Serialization; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.DeclarativeValidation; +using Abblix.Utils.Json; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents an authorization request model containing the necessary properties +/// for an OpenID Connect or OAuth 2.0 authorization request. +/// +public record AuthorizationRequest +{ + /// + /// The requested scopes, which specify the access privileges requested as part of the authorization. + /// Common scopes include 'openid', 'profile', 'email', 'phone', 'address', and 'offline_access'. + /// + [JsonPropertyName(Parameters.Scope)] + [JsonConverter(typeof(SpaceSeparatedValuesConverter))] + [AllowedValues(Scopes.OpenId, Scopes.Profile, Scopes.Email, Scopes.Phone, Scopes.Address, Scopes.OfflineAccess)] + public string[] Scope { get; init; } = Array.Empty(); + + /// + /// The detailed request for specific claims (user attributes) to be included in the ID token or + /// returned from the UserInfo endpoint. + /// + [JsonPropertyName(Parameters.Claims)] + public RequestedClaims? Claims { get; init; } + + /// + /// The requested response types. + /// + [JsonPropertyName(Parameters.ResponseType)] + [JsonConverter(typeof(SpaceSeparatedValuesConverter))] + [AllowedValues(ResponseTypes.Code, ResponseTypes.Token, ResponseTypes.IdToken)] + public string[]? ResponseType { get; init; } + + /// + /// The client ID of the requesting client. + /// + [JsonPropertyName(Parameters.ClientId)] + public string? ClientId { get; init; } + + /// + /// The redirect URI where the response should be sent. + /// + [JsonPropertyName(Parameters.RedirectUri)] + [AbsoluteUri] + public Uri? RedirectUri { get; init; } + + /// + /// The optional state parameter to maintain state between the request and the callback. + /// + [JsonPropertyName(Parameters.State)] + public string? State { get; init; } + + /// + /// The requested response mode. + /// + [JsonPropertyName(Parameters.ResponseMode)] + [AllowedValues(ResponseModes.FormPost, ResponseModes.Fragment, ResponseModes.Query)] + public string? ResponseMode { get; init; } + + /// + /// The nonce value used to associate the client session with an ID token. + /// + [JsonPropertyName(Parameters.Nonce)] + public string? Nonce { get; init; } + + /// + /// Specifies how the authorization server displays the authentication and consent UI. + /// + [JsonPropertyName(Parameters.Display)] + [AllowedValues(DisplayModes.Page, DisplayModes.Popup, DisplayModes.Touch, DisplayModes.Wap)] + public string? Display { get; init; } + + /// + /// The prompt parameter, which specifies whether the authorization server prompts the user for + /// re-authentication and consent. + /// + [JsonPropertyName(Parameters.Prompt)] + [AllowedValues(Prompts.Create, Prompts.Consent, Prompts.Login, Prompts.None, Prompts.SelectAccount)] + public string? Prompt { get; init; } + + /// + /// Specifies the allowable elapsed time since the last user authentication. + /// + [JsonPropertyName(Parameters.MaxAge)] + [JsonConverter(typeof(TimeSpanSecondsConverter))] + public TimeSpan? MaxAge { get; init; } + + /// + /// The UI locales requested for the user interface. + /// + [JsonPropertyName(Parameters.UiLocales)] + [JsonConverter(typeof(ArrayConverter))] + public CultureInfo[]? UiLocales { get; init; } + + /// + /// The claims locales requested for the authorization response. + /// + [JsonPropertyName(Parameters.ClaimsLocales)] + [JsonConverter(typeof(ArrayConverter))] + public CultureInfo[]? ClaimsLocales { get; init; } + + /// + /// The ID token hint, which can be used to pre-fill the authentication and consent UI. + /// + [JsonPropertyName(Parameters.IdTokenHint)] + public string? IdTokenHint { get; init; } + + /// + /// The login hint, which can be used to pre-fill the authentication UI. + /// + [JsonPropertyName(Parameters.LoginHint)] + public string? LoginHint { get; init; } + + /// + /// The Authentication Context Class References requested. + /// + [JsonPropertyName(Parameters.AcrValues)] + [JsonConverter(typeof(SpaceSeparatedValuesConverter))] + public string[]? AcrValues { get; init; } + + /// + /// The code challenge, which is used in the PKCE extension for public clients. + /// + [JsonPropertyName(Parameters.CodeChallenge)] + public string? CodeChallenge { get; init; } + + /// + /// The code challenge method, which specifies the method used to derive the code challenge from the code + /// verifier. + /// + [JsonPropertyName(Parameters.CodeChallengeMethod)] + [AllowedValues(CodeChallengeMethods.Plain, CodeChallengeMethods.S256)] + public string? CodeChallengeMethod { get; init; } + + /// + /// The request as a JWT whose Claims are the request parameters. + /// + [JsonPropertyName(Parameters.Request)] + public string? Request { get; init; } + + /// + /// URL referencing a resource containing a Request Object value, which is a JWT containing the request + /// parameters. + /// + [JsonPropertyName(Parameters.RequestUri)] + [AbsoluteUri(RequireScheme = "https")] + public Uri? RequestUri { get; init; } + + /// + /// Specifies the resource for which the access token is requested. + /// As defined in RFC 8707, this parameter is used to request access tokens with a specific scope for a particular + /// resource. + /// + [JsonPropertyName(Parameters.Resource)] + public Uri[]? Resource { get; set; } + + public static class Parameters + { + public const string Scope = "scope"; + public const string Claims = "claims"; + public const string ResponseType = "response_type"; + public const string ClientId = "client_id"; + public const string RedirectUri = "redirect_uri"; + public const string State = "state"; + public const string ResponseMode = "response_mode"; + public const string Nonce = "nonce"; + public const string Display = "display"; + public const string Prompt = "prompt"; + public const string MaxAge = "max_age"; + public const string UiLocales = "ui_locales"; + public const string ClaimsLocales = "claims_locales"; + public const string IdTokenHint = "id_token_hint"; + public const string LoginHint = "login_hint"; + public const string AcrValues = "acr_values"; + public const string CodeChallenge = "code_challenge"; + public const string CodeChallengeMethod = "code_challenge_method"; + public const string Resource = "resource"; + + public const string Request = "request"; + public const string RequestUri = "request_uri"; + } +} diff --git a/Abblix.Oidc.Server/Model/BackChannelAuthenticationRequest.cs b/Abblix.Oidc.Server/Model/BackChannelAuthenticationRequest.cs new file mode 100644 index 00000000..50ab5de4 --- /dev/null +++ b/Abblix.Oidc.Server/Model/BackChannelAuthenticationRequest.cs @@ -0,0 +1,29 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a back-channel authentication request in scenarios such as OpenID Connect's Client Initiated Backchannel +/// Authentication (CIBA) flow. +/// +public record BackChannelAuthenticationRequest; diff --git a/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs b/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs new file mode 100644 index 00000000..366f4b33 --- /dev/null +++ b/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs @@ -0,0 +1,367 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Abblix.Jwt; +using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.DeclarativeValidation; +using Abblix.Utils.Json; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents metadata for an OAuth2 client based on the OpenID Connect discovery specification. +/// +/// +/// See the OpenID Connect Registration specification at https://openid.net/specs/openid-connect-registration-1_0.html. +/// +public record ClientRegistrationRequest +{ + /// + /// Array of redirection URIs for the OP to redirect the End-User after obtaining authorization. + /// + [JsonPropertyName(Parameters.RedirectUris)] + [Required] + [ElementsRequired] + public Uri[] RedirectUris { get; set; } = default!; + + /// + /// List of OAuth 2.0 response_type values that the client will use. + /// + [JsonPropertyName(Parameters.ResponseTypes)] + [AllowedValues( + Common.Constants.ResponseTypes.Code, + Common.Constants.ResponseTypes.Token, + Common.Constants.ResponseTypes.IdToken)] + [JsonConverter(typeof(ArrayConverter))] + public string[][] ResponseTypes { get; init; } = { new [] { Common.Constants.ResponseTypes.Code } }; + + /// + /// List of OAuth 2.0 grant type values that the client will use. + /// + [JsonPropertyName(Parameters.GrantTypes)] + [AllowedValues( + Common.Constants.GrantTypes.AuthorizationCode, + Common.Constants.GrantTypes.Implicit, + Common.Constants.GrantTypes.RefreshToken)] + public string[] GrantTypes { get; init; } = { Common.Constants.GrantTypes.AuthorizationCode }; + + /// + /// Kind of the application. + /// + [JsonPropertyName(Parameters.ApplicationType)] + public string ApplicationType { get; init; } = ApplicationTypes.Web; + + /// + /// Array of e-mail addresses of people responsible for this client. + /// + [JsonPropertyName(Parameters.Contacts)] + public string[]? Contacts { get; init; } + + /// + /// Desired identifier of the client. + /// + [JsonPropertyName(Parameters.ClientId)] + public string? ClientId { get; init; } + + /// + /// Name of the client. + /// + [JsonPropertyName(Parameters.ClientName)] + public string? ClientName { get; init; } + + /// + /// URL that references a logo for the client. + /// + [JsonPropertyName(Parameters.LogoUri)] + [AbsoluteUri] + public Uri? LogoUri { get; init; } + + /// + /// URL of the home page of the client. + /// + [JsonPropertyName(Parameters.ClientUri)] + [AbsoluteUri] + public Uri? ClientUri { get; init; } + + /// + /// URL that the Relying Party provides to the End-User to read about how the profile data will be used. + /// + [JsonPropertyName(Parameters.PolicyUri)] + [AbsoluteUri] + public Uri? PolicyUri { get; init; } + + /// + /// URL that the Relying Party provides to the End-User to read about the Relying Party's terms of service. + /// + [JsonPropertyName(Parameters.TosUri)] + [AbsoluteUri] + public Uri? TermsOfServiceUri { get; init; } + + /// + /// URL for the client's JSON Web Key Set document. + /// + [JsonPropertyName(Parameters.JwksUri)] + [AbsoluteUri] + public Uri? JwksUri { get; init; } + + /// + /// Client's JSON Web Key Set document value. + /// + [JsonPropertyName(Parameters.Jwks)] + public JsonWebKeySet? Jwks { get; init; } + + /// + /// URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. + /// + [JsonPropertyName(Parameters.SectorIdentifierUri)] + [AbsoluteUri] + public Uri? SectorIdentifierUri { get; init; } + + /// + /// Subject type requested for responses to this client. + /// + [JsonPropertyName(Parameters.SubjectType)] + public string? SubjectType { get; init; } = SubjectTypes.Public; + + /// + /// JWS algorithm for the ID Token issued to this client. + /// + [JsonPropertyName(Parameters.IdTokenSignedResponseAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? IdTokenSignedResponseAlg { get; init; } + + /// + /// JWE algorithm to encrypt the ID Token issued to this client. + /// + [JsonPropertyName(Parameters.IdTokenEncryptedResponseAlg)] + public string? IdTokenEncryptedResponseAlg { get; init; } + + /// + /// JWE encryption method to encrypt the ID Token issued to this client. + /// + [JsonPropertyName(Parameters.IdTokenEncryptedResponseEnc)] + public string? IdTokenEncryptedResponseEnc { get; init; } + + /// + /// JWS algorithm for UserInfo Responses. + /// + [JsonPropertyName(Parameters.UserInfoSignedResponseAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? UserInfoSignedResponseAlg { get; init; } + + /// + /// JWE algorithm to encrypt UserInfo Responses. + /// + [JsonPropertyName(Parameters.UserInfoEncryptedResponseAlg)] + public string? UserInfoEncryptedResponseAlg { get; init; } + + /// + /// JWE encryption method to encrypt the UserInfo Responses. + /// + [JsonPropertyName(Parameters.UserInfoEncryptedResponseEnc)] + public string? UserInfoEncryptedResponseEnc { get; init; } + + /// + /// JWS algorithm that MUST be used for Request Objects sent to the OP. + /// + [JsonPropertyName(Parameters.RequestObjectSigningAlg)] + public string? RequestObjectSigningAlg { get; init; } + + /// + /// JWE algorithm the RP is declaring that it may use for encrypting Request Objects sent to the OP. + /// + [JsonPropertyName(Parameters.RequestObjectEncryptionAlg)] + public string? RequestObjectEncryptionAlg { get; init; } + + /// + /// JWE encryption method the RP is declaring that it may use for encrypting Request Objects sent to the OP. + /// + [JsonPropertyName(Parameters.RequestObjectEncryptionEnc)] + public string? RequestObjectEncryptionEnc { get; init; } + + /// + /// Requested Authentication Method Reference values for this client. + /// + [JsonPropertyName(Parameters.TokenEndpointAuthMethod)] + [AllowedValues( + ClientAuthenticationMethods.ClientSecretBasic, + ClientAuthenticationMethods.ClientSecretPost, + ClientAuthenticationMethods.PrivateKeyJwt, + ClientAuthenticationMethods.None)] + public string TokenEndpointAuthMethod { get; init; } = ClientAuthenticationMethods.ClientSecretBasic; + + /// + /// JWS algorithm that MUST be used for Private Key JWT Client Authentication at the Token Endpoint. + /// + [JsonPropertyName(Parameters.TokenEndpointAuthSigningAlg)] + [AllowedValues(SigningAlgorithms.None, SigningAlgorithms.RS256)] + public string? TokenEndpointAuthSigningAlg { get; init; } + + /// + /// Default Maximum Authentication Age in seconds. + /// + [JsonPropertyName(Parameters.DefaultMaxAge)] + public TimeSpan? DefaultMaxAge { get; init; } + + /// + /// Boolean to require the auth_time claim in the ID Token. + /// + [JsonPropertyName(Parameters.RequireAuthTime)] + public bool? RequireAuthTime { get; init; } + + /// + /// Default ACR values for this client. + /// + [JsonPropertyName(Parameters.DefaultAcrValues)] + public string[]? DefaultAcrValues { get; init; } + + /// + /// URI for the client's Initiate Login URI. + /// + [JsonPropertyName(Parameters.InitiateLoginUri)] + [AbsoluteUri] + public Uri? InitiateLoginUri { get; init; } + + /// + /// Request parameters in the request or request_uri parameters. + /// + [JsonPropertyName(Parameters.RequestUris)] + public Uri[]? RequestUris { get; init; } + + /// + /// Gets or sets a value indicating whether PKCE (Proof Key for Code Exchange) is required. + /// + [JsonPropertyName(Parameters.PkceRequired)] + public bool PkceRequired { get; set; } + + /// + /// Gets or sets a value indicating whether offline access is allowed. + /// + /// + /// true if offline access is allowed; otherwise, false. Defaults to true. + /// + [JsonPropertyName(Parameters.OfflineAccessAllowed)] + public bool OfflineAccessAllowed { get; set; } = true; + + /// + /// Gets or sets the URI for back-channel logout. + /// + /// + /// The URI for the back-channel logout, or null if not specified. + /// + /// + /// This property corresponds to the 'backchannel_logout_uri' parameter in the OpenID Connect specification. + /// It should be an absolute URI as indicated by the attribute. + /// + [JsonPropertyName(Parameters.BackChannelLogoutUri)] + [AbsoluteUri] + public Uri? BackChannelLogoutUri { get; set; } + + /// + /// Gets or sets a value indicating whether the back-channel logout session is required. + /// + /// + /// true if back-channel logout session is required; otherwise, false. + /// + /// + /// This property corresponds to the 'backchannel_logout_session_required' parameter in the OpenID Connect specification. + /// + [JsonPropertyName(Parameters.BackChannelLogoutSessionRequired)] + public bool BackChannelLogoutSessionRequired { get; set; } + + /// + /// Gets or sets the URI for front-channel logout. + /// + /// + /// The URI to be used for front-channel logout, or null if it is not specified. + /// + /// + /// This property corresponds to the 'frontchannel_logout_uri' parameter in the OpenID Connect specification. + /// It defines the URL to which the OpenID Provider will send the User Agent for logout via the front-channel. + /// The URL should be absolute and conform to the URI standard. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutUri)] + [AbsoluteUri] + public Uri? FrontChannelLogoutUri { get; set; } + + /// + /// Gets or sets a value indicating whether the front-channel logout requires a session identifier. + /// + /// + /// true if the front-channel logout requires a session identifier; otherwise, false. + /// + /// + /// This property corresponds to the 'frontchannel_logout_session_required' parameter in the OpenID Connect specification. + /// When set to true, it indicates that the client requires a session identifier to be sent with front-channel logout requests. + /// This is typically used to facilitate logout across multiple sessions or devices. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutSessionRequired)] + public bool FrontChannelLogoutSessionRequired { get; set; } + + [JsonPropertyName(Parameters.PostLogoutRedirectUris)] + [ElementsRequired] + public Uri[] PostLogoutRedirectUris { get; set; } = Array.Empty(); + + public static class Parameters + { + public const string RedirectUris = "redirect_uris"; + public const string ResponseTypes = "response_types"; + public const string GrantTypes = "grant_types"; + public const string ApplicationType = "application_type"; + public const string Contacts = "contacts"; + public const string ClientId = "client_id"; + public const string ClientName = "client_name"; + public const string LogoUri = "logo_uri"; + public const string ClientUri = "client_uri"; + public const string PolicyUri = "policy_uri"; + public const string TosUri = "tos_uri"; + public const string JwksUri = "jwks_uri"; + public const string Jwks = "jwks"; + public const string SectorIdentifierUri = "sector_identifier_uri"; + public const string SubjectType = "subject_type"; + public const string IdTokenSignedResponseAlg = "id_token_signed_response_alg"; + public const string IdTokenEncryptedResponseAlg = "id_token_encrypted_response_alg"; + public const string IdTokenEncryptedResponseEnc = "id_token_encrypted_response_enc"; + public const string UserInfoSignedResponseAlg = "userinfo_signed_response_alg"; + public const string UserInfoEncryptedResponseAlg = "userinfo_encrypted_response_alg"; + public const string UserInfoEncryptedResponseEnc = "userinfo_encrypted_response_enc"; + public const string RequestObjectSigningAlg = "request_object_signing_alg"; + public const string RequestObjectEncryptionAlg = "request_object_encryption_alg"; + public const string RequestObjectEncryptionEnc = "request_object_encryption_enc"; + public const string TokenEndpointAuthMethod = "token_endpoint_auth_method"; + public const string TokenEndpointAuthSigningAlg = "token_endpoint_auth_signing_alg"; + public const string DefaultMaxAge = "default_max_age"; + public const string RequireAuthTime = "require_auth_time"; + public const string DefaultAcrValues = "default_acr_values"; + public const string InitiateLoginUri = "initiate_login_uri"; + public const string RequestUris = "request_uris"; + public const string PkceRequired = "pkce_required"; + public const string OfflineAccessAllowed = "offline_access_allowed"; + public const string BackChannelLogoutUri = "backchannel_logout_uri"; + public const string BackChannelLogoutSessionRequired = "backchannel_logout_session_required"; + public const string FrontChannelLogoutUri = "frontchannel_logout_uri"; + public const string FrontChannelLogoutSessionRequired = "frontchannel_logout_session_required"; + public const string PostLogoutRedirectUris = "post_logout_redirect_uris"; + } +} diff --git a/Abblix.Oidc.Server/Model/ClientRegistrationResponse.cs b/Abblix.Oidc.Server/Model/ClientRegistrationResponse.cs new file mode 100644 index 00000000..c3350961 --- /dev/null +++ b/Abblix.Oidc.Server/Model/ClientRegistrationResponse.cs @@ -0,0 +1,94 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Abblix.Utils.Json; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents the client registration response as defined in the OpenID Connect specification. +/// +public record ClientRegistrationResponse +{ + private static class Parameters + { + public const string ClientId = "client_id"; + public const string ClientIdIssuedAt = "client_id_issued_at"; + + public const string ClientSecret = "client_secret"; + public const string ClientSecretExpiresAt = "client_secret_expires_at"; + + public const string RegistrationAccessToken = "registration_access_token"; + public const string RegistrationClientUri = "registration_client_uri"; + public const string InitiateLoginUri = "initiate_login_uri"; + } + + /// + /// The unique Client Identifier. It must not be currently valid for any other registered client. + /// This identifier is critical for client identification and for securing client-to-server communication. + /// + [Required] + [JsonPropertyName(Parameters.ClientId)] + public string ClientId { get; init; } = default!; + + /// + /// The client secret. This value must not be assigned to multiple clients and is used for authenticating the client with the server. + /// + [JsonPropertyName(Parameters.ClientSecret)] + public string? ClientSecret { get; init; } + + /// + /// The registration access token that can be used at the Client Configuration Endpoint to perform subsequent operations on the client registration. + /// + [JsonPropertyName(Parameters.RegistrationAccessToken)] + public string? RegistrationAccessToken { get; init; } + + /// + /// The location of the Client Configuration Endpoint where the registration access token can be used for subsequent operations. + /// + [JsonPropertyName(Parameters.RegistrationClientUri)] + public Uri? RegistrationClientUri { get; init; } + + /// + /// The time at which the Client Identifier was issued. Represented as the number of seconds since Unix Epoch (1970-01-01T0:0:0Z) in UTC. + /// + [JsonPropertyName(Parameters.ClientIdIssuedAt)] + [JsonConverter(typeof(DateTimeOffsetUnixTimeSecondsConverter))] + public DateTimeOffset? ClientIdIssuedAt { get; init; } + + /// + /// The time at which the client_secret will expire. Represented as the number of seconds since Unix Epoch (1970-01-01T0:0:0Z) in UTC. + /// A value of 0 indicates that the secret does not expire. + /// + [Required] + [JsonPropertyName(Parameters.ClientSecretExpiresAt)] + [JsonConverter(typeof(DateTimeOffsetUnixTimeSecondsConverter))] + public DateTimeOffset ClientSecretExpiresAt { get; init; } + + /// + /// The URI for initiating the client's login process. + /// + [JsonPropertyName(Parameters.InitiateLoginUri)] + public Uri? InitiateLoginUri { get; init; } +} diff --git a/Abblix.Oidc.Server/Model/ClientRequest.cs b/Abblix.Oidc.Server/Model/ClientRequest.cs new file mode 100644 index 00000000..11d9de33 --- /dev/null +++ b/Abblix.Oidc.Server/Model/ClientRequest.cs @@ -0,0 +1,71 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Net.Http.Headers; +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents an abstract model of a request made by a client via a server-to-server call. +/// This record includes all headers and properties that can be used for authentication in various ways. +/// +public record ClientRequest +{ + public static class Parameters + { + public const string ClientId = "client_id"; + public const string ClientSecret = "client_secret"; + public const string ClientAssertionType = "client_assertion_type"; + public const string ClientAssertion = "client_assertion"; + } + + /// + /// The authorization header used for client authentication. This typically contains credentials like bearer tokens. + /// + public AuthenticationHeaderValue? AuthorizationHeader { get; set; } + + /// + /// The client identifier as registered in the authorization server. + /// It is unique to the client and used to identify it in the authentication process. + /// + [JsonPropertyName(Parameters.ClientId)] + public string? ClientId { get; set; } + + /// + /// The client secret, a confidential string used to authenticate the client with the authorization server. + /// + [JsonPropertyName(Parameters.ClientSecret)] + public string? ClientSecret { get; set; } + + /// + /// The assertion type for the client authentication. This is typically used with JWT bearer tokens. + /// + [JsonPropertyName(Parameters.ClientAssertionType)] + public string? ClientAssertionType { get; set; } + + /// + /// The client assertion, often a JWT, used as a credential to authenticate the client to the authorization server. + /// + [JsonPropertyName(Parameters.ClientAssertion)] + public string? ClientAssertion { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/ConfigurationResponse.cs b/Abblix.Oidc.Server/Model/ConfigurationResponse.cs new file mode 100644 index 00000000..1e278db1 --- /dev/null +++ b/Abblix.Oidc.Server/Model/ConfigurationResponse.cs @@ -0,0 +1,281 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents the configuration response detailing the capabilities and endpoints of the OpenID Connect provider. +/// This response includes information about the provider's issuer identifier, key sets, supported endpoints, +/// supported features, and more, enabling clients to dynamically configure themselves to utilize the provider's +/// services. +/// +public record ConfigurationResponse +{ + /// + /// Nested class containing string constants for JSON property names. + /// + public static class Parameters + { + public const string Issuer = "issuer"; + public const string JwksUri = "jwks_uri"; + public const string AuthorizationEndpoint = "authorization_endpoint"; + public const string TokenEndpoint = "token_endpoint"; + public const string UserInfoEndpoint = "userinfo_endpoint"; + public const string EndSessionEndpoint = "end_session_endpoint"; + public const string CheckSessionIframe = "check_session_iframe"; + public const string IntrospectionEndpoint = "introspection_endpoint"; + public const string RevocationEndpoint = "revocation_endpoint"; + public const string RegistrationEndpoint = "registration_endpoint"; + public const string FrontChannelLogoutSupported = "frontchannel_logout_supported"; + public const string FrontChannelLogoutSessionSupported = "frontchannel_logout_session_supported"; + public const string BackChannelLogoutSupported = "backchannel_logout_supported"; + public const string BackChannelLogoutSessionSupported = "backchannel_logout_session_supported"; + public const string ClaimsParameterSupported = "claims_parameter_supported"; + public const string ScopesSupported = "scopes_supported"; + public const string ClaimsSupported = "claims_supported"; + public const string GrantTypesSupported = "grant_types_supported"; + public const string ResponseTypesSupported = "response_types_supported"; + public const string ResponseModesSupported = "response_modes_supported"; + public const string TokenEndpointAuthMethodsSupported = "token_endpoint_auth_methods_supported"; + public const string IdTokenSigningAlgValuesSupported = "id_token_signing_alg_values_supported"; + public const string SubjectTypesSupported = "subject_types_supported"; + public const string CodeChallengeMethodsSupported = "code_challenge_methods_supported"; + public const string PromptValuesSupported = "prompt_values_supported"; + public const string RequestParameterSupported = "request_parameter_supported"; + public const string RequestObjectSigningAlgValuesSupported = "request_object_signing_alg_values_supported"; + public const string UserInfoSigningAlgValuesSupported = "userinfo_signing_alg_values_supported"; + public const string PushedAuthorizationRequestEndpoint = "pushed_authorization_request_endpoint"; + public const string RequirePushedAuthorizationRequests = "require_pushed_authorization_requests"; + public const string RequireSignedRequestObject = "require_signed_request_object"; + } + + /// + /// The issuer identifier, which uniquely identifies the OpenID Provider. This value must be used by clients + /// when issuing requests to the provider to ensure that the request is directed to the correct entity. + /// + [JsonPropertyName(Parameters.Issuer)] + public string Issuer { init; get; } = default!; + + /// + /// The URL of the OpenID Provider's JSON Web Key Set (JWKS) document. This document contains the provider's public + /// keys, enabling clients to verify signatures and encrypt messages to the provider. + /// + [JsonPropertyName(Parameters.JwksUri)] + public Uri? JwksUri { init; get; } + + /// + /// The URL of the authorization endpoint through which the provider handles authentication requests and user consent. + /// + [JsonPropertyName(Parameters.AuthorizationEndpoint)] + public Uri? AuthorizationEndpoint { init; get; } + + /// + /// The URL of the token endpoint where the provider issues tokens (e.g., access tokens, refresh tokens) to clients. + /// + [JsonPropertyName(Parameters.TokenEndpoint)] + public Uri? TokenEndpoint { init; get; } + + /// + /// The URL of the user information endpoint from which the provider returns claims about the authenticated user. + /// + [JsonPropertyName(Parameters.UserInfoEndpoint)] + public Uri? UserInfoEndpoint { init; get; } + + /// + /// The URL of the end session endpoint that supports logging out users from the provider. + /// + [JsonPropertyName(Parameters.EndSessionEndpoint)] + public Uri? EndSessionEndpoint { init; get; } + + /// + /// The URL of the iframe used by the provider to facilitate session management in client applications. + /// + [JsonPropertyName(Parameters.CheckSessionIframe)] + public Uri? CheckSessionIframe { init; get; } + + /// + /// The URL of the revocation endpoint where clients can revoke access tokens or refresh tokens. + /// + [JsonPropertyName(Parameters.RevocationEndpoint)] + public Uri? RevocationEndpoint { init; get; } + + /// + /// The URL of the introspection endpoint where clients can obtain information about tokens' current state. + /// + [JsonPropertyName(Parameters.IntrospectionEndpoint)] + public Uri? IntrospectionEndpoint { init; get; } + + /// + /// The URL of the client registration endpoint that supports dynamic registration of client applications. + /// + [JsonPropertyName(Parameters.RegistrationEndpoint)] + public Uri? RegistrationEndpoint { init; get; } + + /// + /// The URL for the Pushed Authorization Request endpoint, which allows clients to pre-register authorization requests. + /// + [JsonPropertyName(Parameters.PushedAuthorizationRequestEndpoint)] + public Uri? PushedAuthorizationRequestEndpoint { get; set; } + + /// + /// Indicates whether the provider requires clients to use the Pushed Authorization Requests (PAR) only. + /// + [JsonPropertyName(Parameters.RequirePushedAuthorizationRequests)] + public bool RequirePushedAuthorizationRequests { get; set; } //TODO use it! + + /// + /// Indicates whether the OpenID Provider supports front channel logout, allowing clients to log out users + /// across multiple applications. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutSupported)] + public bool? FrontChannelLogoutSupported { init; get; } + + /// + /// Indicates whether the OpenID Provider supports session management for front channel logout, + /// enabling clients to be notified what user log out. + /// + [JsonPropertyName(Parameters.FrontChannelLogoutSessionSupported)] + public bool? FrontChannelLogoutSessionSupported { init; get; } + + /// + /// Indicates whether the OpenID Provider supports back channel logout, allowing clients to log out users + /// through direct back-channel communication. + /// + [JsonPropertyName(Parameters.BackChannelLogoutSupported)] + public bool? BackChannelLogoutSupported { init; get; } + + /// + /// Indicates whether the OpenID Provider supports session management for back channel logout, + /// enabling secure and direct notification of user logout. + /// + [JsonPropertyName(Parameters.BackChannelLogoutSessionSupported)] + public bool? BackChannelLogoutSessionSupported { init; get; } + + /// + /// Indicates whether the OpenID Provider supports the use of the claims parameter, + /// allowing clients to request specific claims in the ID token. + /// + [JsonPropertyName(Parameters.ClaimsParameterSupported)] + public bool? ClaimsParameterSupported { init; get; } + + /// + /// Lists the scopes supported by the OpenID Provider, defining the extent of access granted by the authorization. + /// + [JsonPropertyName(Parameters.ScopesSupported)] + public IEnumerable ScopesSupported { init; get; } = default!; + + /// + /// Lists the claims supported by the OpenID Provider, indicating the user information that can be included + /// in the ID token or obtained from the UserInfo endpoint. + /// + [JsonPropertyName(Parameters.ClaimsSupported)] + public IEnumerable ClaimsSupported { init; get; } = default!; + + /// + /// Lists the grant types supported by the OpenID Provider, defining the methods through which + /// clients can request tokens. + /// + [JsonPropertyName(Parameters.GrantTypesSupported)] + public IEnumerable GrantTypesSupported { init; get; } = default!; + + /// + /// Lists the response types supported by the OpenID Provider, indicating the formats that can be used + /// for the authorization response. + /// + [JsonPropertyName(Parameters.ResponseTypesSupported)] + public IEnumerable ResponseTypesSupported { init; get; } = default!; + + /// + /// Lists the response modes supported by the OpenID Provider, defining how the authorization response + /// should be delivered to the client. + /// + [JsonPropertyName(Parameters.ResponseModesSupported)] + public IEnumerable ResponseModesSupported { init; get; } = default!; + + /// + /// Lists the token endpoint authentication methods supported by the OpenID Provider, + /// specifying how clients authenticate to the token endpoint. + /// + [JsonPropertyName(Parameters.TokenEndpointAuthMethodsSupported)] + public IEnumerable TokenEndpointAuthMethodsSupported { init; get; } = default!; + + /// + /// Lists the ID token signing algorithm values supported by the OpenID Provider, + /// indicating the algorithms that can be used to sign the ID token. + /// + [JsonPropertyName(Parameters.IdTokenSigningAlgValuesSupported)] + public IEnumerable IdTokenSigningAlgValuesSupported { init; get; } = default!; + + /// + /// Lists the subject types supported by the OpenID Provider, + /// defining how the subject's identifier is formatted and presented to the client. + /// + [JsonPropertyName(Parameters.SubjectTypesSupported)] + public IEnumerable SubjectTypesSupported { init; get; } = default!; + + /// + /// Lists the code challenge methods supported by the OpenID Provider for PKCE, + /// enhancing the security of the authorization code flow. + /// + [JsonPropertyName(Parameters.CodeChallengeMethodsSupported)] + public IEnumerable CodeChallengeMethodsSupported { init; get; } = default!; + + /// + /// Indicates whether the OpenID Provider supports the use of the request parameter, + /// enabling clients to send a fully self-contained authorization request. + /// + [JsonPropertyName(Parameters.RequestParameterSupported)] + public bool RequestParameterSupported { init; get; } = default!; + + /// + /// Lists the prompt values supported by the OpenID Provider, + /// specifying how the provider should prompt the user during authentication. + /// + [JsonPropertyName(Parameters.PromptValuesSupported)] + public IEnumerable PromptValuesSupported { init; get; } = default!; + + /// + /// Specifies the signing algorithms supported by the OpenID Provider for user information endpoints. + /// These algorithms are used to sign the data returned from UserInfo endpoints, ensuring data integrity + /// and authentication of the information source. + /// + [JsonPropertyName(Parameters.UserInfoSigningAlgValuesSupported)] + public IEnumerable? UserInfoSigningAlgValuesSupported { init; get; } = default!; + + /// + /// Specifies the signing algorithms supported by the OpenID Provider for request objects. + /// These algorithms are used to sign request objects sent to the OpenID Provider, providing + /// security measures against tampering and ensuring the authenticity of the request. + /// + [JsonPropertyName(Parameters.RequestObjectSigningAlgValuesSupported)] + public IEnumerable? RequestObjectSigningAlgValuesSupported { init; get; } = default!; + + /// + /// Indicates whether the OpenID Provider mandates that all request objects must be signed. + /// This requirement ensures an additional layer of security by verifying the authenticity and + /// integrity of request objects received by the provider. + /// + [JsonPropertyName(Parameters.RequireSignedRequestObject)] + public bool RequireSignedRequestObject { init; get; } //TODO use it! +} diff --git a/Abblix.Oidc.Server/Model/EndSessionRequest.cs b/Abblix.Oidc.Server/Model/EndSessionRequest.cs new file mode 100644 index 00000000..cb8cb0c3 --- /dev/null +++ b/Abblix.Oidc.Server/Model/EndSessionRequest.cs @@ -0,0 +1,88 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using System.Text.Json.Serialization; +using Abblix.Oidc.Server.DeclarativeValidation; + + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a request to end a user session, commonly used in OpenID Connect logout scenarios. +/// +public record EndSessionRequest +{ + public static class Parameters + { + public const string IdTokenHint = "id_token_hint"; + public const string LogoutHint = "logout_hint"; + public const string ClientId = "client_id"; + public const string PostLogoutRedirectUri = "post_logout_redirect_uri"; + public const string State = "state"; + public const string UiLocales = "ui_locales"; + public const string Confirmed = "confirmed"; + } + + /// + /// The ID token hint. A previously issued ID token passed to the logout endpoint to hint about the End-User's current authenticated session. + /// + [JsonPropertyName(Parameters.IdTokenHint)] + public string? IdTokenHint { get; set; } + + /// + /// The logout hint. An optional parameter used to indicate the login identifier or username the user might have used. + /// + [JsonPropertyName(Parameters.LogoutHint)] + public string? LogoutHint { get; set; } + + /// + /// The client identifier for which the logout request is made. + /// + [JsonPropertyName(Parameters.ClientId)] + public string? ClientId { get; set; } + + /// + /// The state parameter to maintain state between the logout request and the callback to the client after logout. + /// + [JsonPropertyName(Parameters.State)] + public string? State { get; set; } + + /// + /// The URL to which the user should be redirected after logout. This URI must be registered with the authorization server. + /// + [JsonPropertyName(Parameters.PostLogoutRedirectUri)] + [AbsoluteUri] + public Uri? PostLogoutRedirectUri { get; set; } + + /// + /// The preferred user interface locales for the logout page, represented as a list of . + /// + [JsonPropertyName(Parameters.UiLocales)] + public IEnumerable? UiLocales { get; set; } + + /// + /// A boolean value indicating whether the end session has been confirmed. Typically used in interactive logout confirmation scenarios. + /// + [JsonPropertyName(Parameters.Confirmed)] + public bool Confirmed { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/ErrorResponse.cs b/Abblix.Oidc.Server/Model/ErrorResponse.cs new file mode 100644 index 00000000..089da54b --- /dev/null +++ b/Abblix.Oidc.Server/Model/ErrorResponse.cs @@ -0,0 +1,45 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a standardized error response, commonly used in web APIs and OAuth2/OpenID Connect protocols. +/// +public record ErrorResponse(string Error, string ErrorDescription) +{ + /// + /// The error code representing the specific type of error encountered. + /// This is a single ASCII error code from the predefined set of OAuth2/OpenID Connect standard codes. + /// + [JsonPropertyName("error")] + public string Error { get; init; } = Error; + + /// + /// A human-readable text providing additional information about the error. + /// This description is meant to aid in diagnosing the error and is not intended for displaying to end-users. + /// + [JsonPropertyName("error_description")] + public string ErrorDescription { get; init; } = ErrorDescription; +} diff --git a/Abblix.Oidc.Server/Model/IntrospectionRequest.cs b/Abblix.Oidc.Server/Model/IntrospectionRequest.cs new file mode 100644 index 00000000..dba5c04f --- /dev/null +++ b/Abblix.Oidc.Server/Model/IntrospectionRequest.cs @@ -0,0 +1,57 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a request for OAuth 2.0 token introspection, allowing clients to query the authorization server +/// about the state and details of a token. +/// Inherits from . +/// +public record IntrospectionRequest +{ + public static class Parameters + { + public const string Token = "token"; + public const string TokenTypeHint = "token_type_hint"; + } + + /// + /// The token that the client wants to introspect. + /// This property is and should contain the string value of the token. + /// + [JsonPropertyName(Parameters.Token)] + [Required] + public string Token { get; set; } = default!; + + /// + /// A hint about the type of the token submitted for introspection. + /// This property is optional and can be used to optimize the introspection process. + /// The value can be standardized token type values such as "access_token" or "refresh_token". + /// + [JsonPropertyName(Parameters.TokenTypeHint)] + public string? TokenTypeHint { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/ReadClientSuccessfulResponse.cs b/Abblix.Oidc.Server/Model/ReadClientSuccessfulResponse.cs new file mode 100644 index 00000000..ac8331ec --- /dev/null +++ b/Abblix.Oidc.Server/Model/ReadClientSuccessfulResponse.cs @@ -0,0 +1,180 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; +using Abblix.Oidc.Server.DeclarativeValidation; +using Abblix.Oidc.Server.Endpoints.DynamicClientManagement.Interfaces; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents the response for a successful client read request, +/// detailing the configuration and metadata of an OAuth or OpenID Connect client. +/// +public record ReadClientSuccessfulResponse : ReadClientResponse +{ + /// + /// The unique identifier of the client as registered with the authorization server. + /// + [JsonPropertyOrder(1)] + [JsonPropertyName(Parameters.ClientId)] + public string ClientId { get; init; } = default!; + + /// + /// The secret associated with the client, used for authenticating with the authorization server. + /// + [JsonPropertyOrder(2)] + [JsonPropertyName(Parameters.ClientSecret)] + public string ClientSecret { get; init; } = default!; + + /// + /// The expiration time of the client secret. Indicates when the client secret will become invalid. + /// + [JsonPropertyOrder(3)] + [JsonPropertyName(Parameters.ClientSecretExpiresAt)] + public DateTimeOffset ClientSecretExpiresAt { get; init; } + + /// + /// The URI where client configuration information can be accessed. + /// + [JsonPropertyOrder(4)] + [JsonPropertyName(Parameters.RegistrationClientUri)] + public Uri RegistrationClientUri { get; init; } = default!; + + /// + /// The method used for authenticating the client at the token endpoint. + /// + [JsonPropertyOrder(5)] + [JsonPropertyName(Parameters.TokenEndpointAuthMethod)] + public string TokenEndpointAuthMethod { get; init; } = default!; + + /// + /// The type of application for which the client is registered (e.g., web, native). + /// + [JsonPropertyOrder(6)] + [JsonPropertyName(Parameters.ApplicationType)] + public string ApplicationType { get; init; } = default!; + + /// + /// The URIs where the client expects to receive responses after user authentication. + /// + [JsonPropertyOrder(7)] + [JsonPropertyName(Parameters.RedirectUris)] + public Uri[] RedirectUris { get; init; } = default!; + + /// + /// The name of the client. + /// + [JsonPropertyOrder(8)] + [JsonPropertyName(Parameters.ClientName)] + public string ClientName { get; init; } = default!; + + /// + /// The name of the client. + /// + [JsonPropertyOrder(10)] + [JsonPropertyName(Parameters.LogoUri)] + [AbsoluteUri] + public Uri LogoUri { get; init; } = default!; + + /// + /// The type of subjects used (e.g., public, pairwise). + /// + [JsonPropertyOrder(11)] + [JsonPropertyName(Parameters.SubjectType)] + public string SubjectType { get; init; } = default!; + + /// + /// The URI identifying the sector that the client is a part of. + /// + [JsonPropertyOrder(12)] + [JsonPropertyName(Parameters.SectorIdentifierUri)] + [AbsoluteUri] + public Uri SectorIdentifierUri { get; init; } = default!; + + /// + /// The URI to the client's JSON Web Key Set document. + /// + [JsonPropertyOrder(13)] + [JsonPropertyName(Parameters.JwksUri)] + public Uri JwksUri { get; init; } = default!; + + /// + /// The algorithm used for encrypting the user information response. + /// + [JsonPropertyOrder(14)] + [JsonPropertyName(Parameters.UserInfoEncryptedResponseAlg)] + public string UserInfoEncryptedResponseAlg { get; init; } = default!; + + /// + /// The encryption encoding used for the user information response. + /// + [JsonPropertyOrder(15)] + [JsonPropertyName(Parameters.UserInfoEncryptedResponseEnc)] + public string UserInfoEncryptedResponseEnc { get; init; } = default!; + + /// + /// The contacts for the client, typically email addresses. + /// + [JsonPropertyOrder(16)] + [JsonPropertyName(Parameters.Contacts)] + public string[] Contacts { get; init; } = default!; + + /// + /// The URIs for any request objects associated with the client. + /// + [JsonPropertyOrder(17)] + [JsonPropertyName(Parameters.RequestUris)] + public Uri[] RequestUris { get; init; } = default!; + + /// + /// The URI for initiating the client's login process. + /// + [JsonPropertyOrder(18)] + [JsonPropertyName(Parameters.InitiateLoginUri)] + [AbsoluteUri] + public Uri? InitiateLoginUri { get; init; } + + /// + /// Contains constants for parameter names. + /// + private static class Parameters + { + public const string ClientId = "client_id"; + public const string ClientSecret = "client_secret"; + public const string ClientSecretExpiresAt = "client_secret_expires_at"; + public const string RegistrationClientUri = "registration_client_uri"; + public const string TokenEndpointAuthMethod = "token_endpoint_auth_method"; + public const string ApplicationType = "application_type"; + public const string RedirectUris = "redirect_uris"; + public const string ClientName = "client_name"; + public const string LogoUri = "logo_uri"; + public const string SubjectType = "subject_type"; + public const string SectorIdentifierUri = "sector_identifier_uri"; + public const string JwksUri = "jwks_uri"; + public const string UserInfoEncryptedResponseAlg = "userinfo_encrypted_response_alg"; + public const string UserInfoEncryptedResponseEnc = "userinfo_encrypted_response_enc"; + public const string Contacts = "contacts"; + public const string RequestUris = "request_uris"; + public const string InitiateLoginUri = "initiate_login_uri"; + } +} diff --git a/Abblix.Oidc.Server/Model/RequestedClaimDetails.cs b/Abblix.Oidc.Server/Model/RequestedClaimDetails.cs new file mode 100644 index 00000000..fc3a50e4 --- /dev/null +++ b/Abblix.Oidc.Server/Model/RequestedClaimDetails.cs @@ -0,0 +1,53 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents the details of a requested claim in OAuth2 or OpenID Connect scenarios. +/// This can include whether the claim is essential, and specific values or a range of values for the claim. +/// +public record RequestedClaimDetails +{ + /// + /// Indicates whether the claim is essential for the authorization process. + /// If true, the claim is essential and should be provided by the user for successful authorization. + /// + [JsonPropertyName("essential")] + public bool? Essential { get; init; } + + /// + /// Specifies the specific value the claim should have. + /// This property is used when a particular value for the claim is for processing. + /// + [JsonPropertyName("value")] + public object? Value { get; init; } + + /// + /// Specifies a set of acceptable values for the claim. + /// This property is used when multiple values are acceptable for the claim. + /// + [JsonPropertyName("values")] + public object[]? Values { get; init; } +} diff --git a/Abblix.Oidc.Server/Model/RequestedClaims.cs b/Abblix.Oidc.Server/Model/RequestedClaims.cs new file mode 100644 index 00000000..95d9cafc --- /dev/null +++ b/Abblix.Oidc.Server/Model/RequestedClaims.cs @@ -0,0 +1,45 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents claims requested for inclusion in the UserInfo response and ID Token in OAuth2 or OpenID Connect authentication flows. +/// +public record RequestedClaims +{ + /// + /// A collection of claims requested to be included in the UserInfo response. + /// Each entry in the dictionary represents a claim with its corresponding details, such as whether the claim is essential and specific value requirements. + /// + [JsonPropertyName("userinfo")] + public Dictionary? UserInfo { get; init; } + + /// + /// A collection of claims requested to be included in the ID Token. + /// Similar to UserInfo, each entry in the dictionary specifies a claim and its associated details, including essentiality and value constraints. + /// + [JsonPropertyName("id_token")] + public Dictionary? IdToken { get; init; } +} diff --git a/Abblix.Oidc.Server/Model/RevocationRequest.cs b/Abblix.Oidc.Server/Model/RevocationRequest.cs new file mode 100644 index 00000000..6ffc5f1f --- /dev/null +++ b/Abblix.Oidc.Server/Model/RevocationRequest.cs @@ -0,0 +1,54 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a request to revoke a token, typically used in OAuth 2.0 token revocation scenarios. +/// Inherits from . +/// +public record RevocationRequest +{ + public static class Parameters + { + public const string Token = "token"; + public const string TokenTypeHint = "token_type_hint"; + } + + /// + /// The token that the client wants to revoke. + /// + [JsonPropertyName(Parameters.Token)] + [Required] + public string Token { get; set; } = default!; + + /// + /// A hint about the type of the token submitted for revocation. + /// This property can help the authorization server to optimize the revocation process. + /// + [JsonPropertyName(Parameters.TokenTypeHint)] + public string? TokenTypeHint { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/TokenRequest.cs b/Abblix.Oidc.Server/Model/TokenRequest.cs new file mode 100644 index 00000000..a8d8e8bc --- /dev/null +++ b/Abblix.Oidc.Server/Model/TokenRequest.cs @@ -0,0 +1,120 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Abblix.Oidc.Server.Common.Constants; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a request for obtaining various types of tokens (e.g., access token, refresh token) from the authorization server. +/// +public record TokenRequest +{ + public static class Parameters + { + public const string GrantType = "grant_type"; + public const string Code = "code"; + public const string RedirectUri = "redirect_uri"; + public const string Resource = "resource"; + public const string RefreshToken = "refresh_token"; + public const string Scope = "scope"; + public const string Username = "username"; + public const string Password = "password"; + public const string CodeVerifier = "code_verifier"; + } + + /// + /// The grant type of the token request, indicating the method being used to obtain the token. + /// Common values include 'authorization_code', 'refresh_token', 'password', etc. + /// + [JsonPropertyName(Parameters.GrantType)] + [Required] + [AllowedValues( + GrantTypes.AuthorizationCode, + GrantTypes.RefreshToken, + GrantTypes.Password, + GrantTypes.Ciba, + GrantTypes.Implicit, + GrantTypes.ClientCredentials)] + public string GrantType { get; set; } = default!; + + /// + /// The authorization code received from the authorization server. This is used in the authorization code grant type. + /// + [JsonPropertyName(Parameters.Code)] + public string? Code { get; set; } + + /// + /// The redirect URI where the response will be sent. This must match the redirect URI registered with the authorization server. + /// + [JsonPropertyName(Parameters.RedirectUri)] + public Uri? RedirectUri { get; set; } + + /// + /// The resource for which the access token is being requested. + /// This is optional and is used in scenarios such as OAuth 2.0 for APIs. + /// + /// + /// Defined in RFC 8707. + /// + [JsonPropertyName(Parameters.Resource)] + public Uri[]? Resource { get; set; } + + /// + /// The refresh token used to obtain a new access token. Required for the refresh token grant type. + /// + [JsonPropertyName(Parameters.RefreshToken)] + public string? RefreshToken { get; set; } + + /// + /// The scope of the access request, expressed as a list of space-delimited, case-sensitive strings. + /// + [JsonPropertyName(Parameters.Scope)] + [AllowedValues( + Scopes.OpenId, + Scopes.Profile, + Scopes.Email, + Scopes.Phone, + Scopes.OfflineAccess)] + public string[] Scope { get; set; } = Array.Empty(); + + /// + /// The username of the resource owner. Required for the password grant type. + /// + [JsonPropertyName(Parameters.Username)] + public string? UserName { get; set; } + + /// + /// The password of the resource owner. Required for the password grant type. + /// + [JsonPropertyName(Parameters.Password)] + public string? Password { get; set; } + + /// + /// The code verifier for the PKCE (Proof Key for Code Exchange) process. + /// Required for public clients using the authorization code grant type. + /// + [JsonPropertyName(Parameters.CodeVerifier)] + public string? CodeVerifier { get; set; } +} diff --git a/Abblix.Oidc.Server/Model/UserInfoRequest.cs b/Abblix.Oidc.Server/Model/UserInfoRequest.cs new file mode 100644 index 00000000..b95e8a3a --- /dev/null +++ b/Abblix.Oidc.Server/Model/UserInfoRequest.cs @@ -0,0 +1,44 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json.Serialization; + +namespace Abblix.Oidc.Server.Model; + +/// +/// Represents a request to retrieve user information, typically used in OpenID Connect flows. +/// Inherits from . +/// +public record UserInfoRequest +{ + public static class Parameters + { + public const string AccessToken = "access_token"; + } + + /// + /// The access token that authorizes the request to retrieve user information. + /// This token is typically obtained during the authentication process and is used to access protected user data. + /// + [JsonPropertyName(Parameters.AccessToken)] + public string? AccessToken { get; set; } +} diff --git a/Abblix.Oidc.Server/Properties/launchSettings.json b/Abblix.Oidc.Server/Properties/launchSettings.json new file mode 100644 index 00000000..31c1d01c --- /dev/null +++ b/Abblix.Oidc.Server/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Abblix.Oidc.Server": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:50521;http://localhost:50522" + } + } +} \ No newline at end of file diff --git a/Abblix.Oidc.Server/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..3f0dda44 --- /dev/null +++ b/Abblix.Oidc.Server/ServiceCollectionExtensions.cs @@ -0,0 +1,155 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using Abblix.Oidc.Server.Common.Configuration; +using Abblix.Oidc.Server.Endpoints; +using Abblix.Oidc.Server.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Abblix.Oidc.Server; + +/// +/// Extension methods for integrating OpenID Connect (OIDC) core services into an application's service collection. +/// +/// +/// These methods facilitate the setup of essential components for implementing OIDC authentication and +/// authorization flows, such as token issuance, client authentication, and session management. +/// By calling these extension methods, developers can configure and customize the OIDC server according +/// to their application's security requirements and user management policies. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers the core OIDC server services and configurations into the provided service collection. + /// + /// The IServiceCollection instance to which the OIDC services are added. + /// A delegate to configure OIDC server options, allowing customization of settings + /// like issuer URL, token lifetimes, and supported grant types. + /// The updated IServiceCollection instance, enabling chaining of further service registrations. + public static IServiceCollection AddOidcCore(this IServiceCollection services, Action configureOptions) + { + return services.AddOidcCore((options, _) => configureOptions(options)); + } + + /// + /// Adds OIDC server core services to the service collection with additional access to the service provider + /// for more complex configuration scenarios. + /// + /// The IServiceCollection to enhance with OIDC services. + /// A delegate that configures OIDC options with access to the service provider, + /// allowing for dynamic configurations based on other registered services. + /// The IServiceCollection enabling further configurations. + /// + /// This overload provides flexibility to access other services during the OIDC configuration, + /// such as dynamic issuer discovery or conditional service registrations based on the environment or other services. + /// + public static IServiceCollection AddOidcCore( + this IServiceCollection services, + Action configureOptions) + { + return services + .AddOptions() + .Configure(configureOptions).Services + .AddCommonServices() + .AddEndpoints() + .AddFeatures(); + } + + /// + /// Registers a comprehensive set of services related to client authentication, information management, + /// issuer identification, token services, JWT handling, session management, random value generation + /// and logout notifications. + /// + /// + /// This method serves as a convenience wrapper that aggregates the registration of various foundational services + /// necessary for the application's security and functionality. + /// + /// It includes: + /// - Client authentication mechanisms. + /// - Client information management. + /// - Issuer identification. + /// - Token generation, validation and management services. + /// - JSON Web Token (JWT) support. + /// - Session management capabilities. + /// - Random value generators for security tokens and identifiers. + /// - Logout notification mechanisms. + /// + /// By invoking this method, an application ensures that all critical security and operational features + /// are configured and ready for use. + /// + /// The to configure with essential features. + /// The configured , allowing for further chaining of service registrations. + public static IServiceCollection AddFeatures(this IServiceCollection services) + { + return services + .AddLicenseFromOptions() + .AddClientAuthentication() + .AddClientInformation() + .AddIssuer() + .AddTokenServices() + .AddSessionManagement() + .AddRandomGenerators() + .AddLogoutNotification() + .AddStorages() + .AddUserInfo(); + } + + /// + /// Configures the service collection with a comprehensive suite of endpoints necessary for handling various + /// OAuth 2.0 and OpenID Connect flows, including authorization, token issuance, revocation, introspection, + /// user information, session management and dynamic client registration. + /// + /// + /// By calling this method, the application integrates support for: + /// + /// - The Authorization Endpoint for initiating user authentication and consent. + /// - Pushed Authorization Request (PAR) Endpoint for pre-registering authorization requests. + /// - Token Endpoint for issuing tokens following successful authentication. + /// - Revocation Endpoint for token revocation by clients. + /// - Introspection Endpoint for token validation by resource servers. + /// - User Info Endpoint for accessing authenticated user information. + /// - End Session Endpoint for managing user logout processes. + /// - Back Channel Authentication Endpoint for supporting CIBA (Client Initiated Backchannel Authentication). + /// - Check Session Endpoint for session state management in browser clients. + /// - Dynamic Client Registration Endpoint for runtime registration of new clients. + /// + /// This setup ensures the application is equipped to support a wide range of authentication, authorization + /// and session management scenarios in a secure and standards-compliant manner. + /// + /// The to configure with necessary endpoints. + /// The configured , enabling further service registration chaining. + public static IServiceCollection AddEndpoints(this IServiceCollection services) + { + return services + .AddAuthorizationEndpoint() + .AddPushedAuthorizationEndpoint() + .AddTokenEndpoint() + .AddRevocationEndpoint() + .AddIntrospectionEndpoint() + .AddUserInfoEndpoint() + .AddEndSessionEndpoint() + .AddBackChannelAuthenticationEndpoint() + .AddCheckSessionEndpoint() + .AddDynamicClientEndpoints(sp => sp.GetRequiredService>().Value.NewClientOptions); + } +} diff --git a/Abblix.Oidc.sln b/Abblix.Oidc.sln new file mode 100644 index 00000000..2feab164 --- /dev/null +++ b/Abblix.Oidc.sln @@ -0,0 +1,82 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33723.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.DependencyInjection", "Abblix.DependencyInjection\Abblix.DependencyInjection.csproj", "{6EBC9BF3-506A-4EA1-93E7-15F2E1E8A7E0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.Jwt", "Abblix.Jwt\Abblix.Jwt.csproj", "{9017323B-D85F-46FC-8561-07BA41EFD665}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.Oidc.Server", "Abblix.Oidc.Server\Abblix.Oidc.Server.csproj", "{EA342C7F-0C31-4F8F-9983-66B61A8853E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.Oidc.Server.Tests", "Abblix.Oidc.Server.Tests\Abblix.Oidc.Server.Tests.csproj", "{9FB96BFA-8D23-42BA-BB2B-C50E2903CD6A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.Oidc.Server.UnitTests", "Abblix.Oidc.Server.UnitTests\Abblix.Oidc.Server.UnitTests.csproj", "{FAF56FB7-12B5-4AAA-BCA5-A76D49F0E273}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abblix.Utils", "Abblix.Utils\Abblix.Utils.csproj", "{1281A1E9-59D5-4669-9FCA-2668A373319D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AEC498B7-D15B-484A-8735-0AC77D23DC4E}" + ProjectSection(SolutionItems) = preProject + .gitattributes = .gitattributes + .gitignore = .gitignore + .github\workflows\ci-cd.yml = .github\workflows\ci-cd.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abblix.Utils.UnitTests", "Abblix.Utils.UnitTests\Abblix.Utils.UnitTests.csproj", "{B4F3431F-A71C-44F4-A2BF-DCB00D17739E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abblix.Jwt.UnitTests", "Abblix.Jwt.UnitTests\Abblix.Jwt.UnitTests.csproj", "{A68A5E5E-D478-45D9-9C76-412AA07A5F5C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abblix.Oidc.Server.Mvc", "Abblix.Oidc.Server.Mvc\Abblix.Oidc.Server.Mvc.csproj", "{D44DD4A8-9A4D-4BB3-B590-1F9D85A41DB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6EBC9BF3-506A-4EA1-93E7-15F2E1E8A7E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EBC9BF3-506A-4EA1-93E7-15F2E1E8A7E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EBC9BF3-506A-4EA1-93E7-15F2E1E8A7E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EBC9BF3-506A-4EA1-93E7-15F2E1E8A7E0}.Release|Any CPU.Build.0 = Release|Any CPU + {9017323B-D85F-46FC-8561-07BA41EFD665}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9017323B-D85F-46FC-8561-07BA41EFD665}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9017323B-D85F-46FC-8561-07BA41EFD665}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9017323B-D85F-46FC-8561-07BA41EFD665}.Release|Any CPU.Build.0 = Release|Any CPU + {EA342C7F-0C31-4F8F-9983-66B61A8853E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA342C7F-0C31-4F8F-9983-66B61A8853E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA342C7F-0C31-4F8F-9983-66B61A8853E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA342C7F-0C31-4F8F-9983-66B61A8853E5}.Release|Any CPU.Build.0 = Release|Any CPU + {9FB96BFA-8D23-42BA-BB2B-C50E2903CD6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FB96BFA-8D23-42BA-BB2B-C50E2903CD6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FB96BFA-8D23-42BA-BB2B-C50E2903CD6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FB96BFA-8D23-42BA-BB2B-C50E2903CD6A}.Release|Any CPU.Build.0 = Release|Any CPU + {FAF56FB7-12B5-4AAA-BCA5-A76D49F0E273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAF56FB7-12B5-4AAA-BCA5-A76D49F0E273}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAF56FB7-12B5-4AAA-BCA5-A76D49F0E273}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAF56FB7-12B5-4AAA-BCA5-A76D49F0E273}.Release|Any CPU.Build.0 = Release|Any CPU + {1281A1E9-59D5-4669-9FCA-2668A373319D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1281A1E9-59D5-4669-9FCA-2668A373319D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1281A1E9-59D5-4669-9FCA-2668A373319D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1281A1E9-59D5-4669-9FCA-2668A373319D}.Release|Any CPU.Build.0 = Release|Any CPU + {B4F3431F-A71C-44F4-A2BF-DCB00D17739E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4F3431F-A71C-44F4-A2BF-DCB00D17739E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4F3431F-A71C-44F4-A2BF-DCB00D17739E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4F3431F-A71C-44F4-A2BF-DCB00D17739E}.Release|Any CPU.Build.0 = Release|Any CPU + {A68A5E5E-D478-45D9-9C76-412AA07A5F5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A68A5E5E-D478-45D9-9C76-412AA07A5F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A68A5E5E-D478-45D9-9C76-412AA07A5F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A68A5E5E-D478-45D9-9C76-412AA07A5F5C}.Release|Any CPU.Build.0 = Release|Any CPU + {D44DD4A8-9A4D-4BB3-B590-1F9D85A41DB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D44DD4A8-9A4D-4BB3-B590-1F9D85A41DB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D44DD4A8-9A4D-4BB3-B590-1F9D85A41DB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D44DD4A8-9A4D-4BB3-B590-1F9D85A41DB2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DEBC3C65-164F-4F76-B5D3-477C7DA579AA} + EndGlobalSection +EndGlobal diff --git a/Abblix.Oidc.sln.DotSettings b/Abblix.Oidc.sln.DotSettings new file mode 100644 index 00000000..e350da63 --- /dev/null +++ b/Abblix.Oidc.sln.DotSettings @@ -0,0 +1,3 @@ + + IP + True \ No newline at end of file diff --git a/Abblix.Utils.UnitTests/Abblix.Utils.UnitTests.csproj b/Abblix.Utils.UnitTests/Abblix.Utils.UnitTests.csproj new file mode 100644 index 00000000..8029ec63 --- /dev/null +++ b/Abblix.Utils.UnitTests/Abblix.Utils.UnitTests.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Abblix.Utils.UnitTests/Base32Tests.cs b/Abblix.Utils.UnitTests/Base32Tests.cs new file mode 100644 index 00000000..30f305c9 --- /dev/null +++ b/Abblix.Utils.UnitTests/Base32Tests.cs @@ -0,0 +1,113 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text; + +namespace Abblix.Utils.UnitTests; + +public class Base32Tests +{ + private static byte[]? ToBytes(string? input) + => input != null ? Encoding.ASCII.GetBytes(input) : null; + + [Theory] + [InlineData(null, true,"")] + [InlineData("", true,"")] + [InlineData("f", true,"MY======")] + [InlineData("fo", true,"MZXQ====")] + [InlineData("foo", true,"MZXW6===")] + [InlineData("foob", true,"MZXW6YQ=")] + [InlineData("fooba", true,"MZXW6YTB")] + [InlineData("foobar", true,"MZXW6YTBOI======")] + [InlineData("f", false,"MY")] + [InlineData("fo", false,"MZXQ")] + [InlineData("foo", false,"MZXW6")] + [InlineData("foob", false,"MZXW6YQ")] + [InlineData("fooba", false,"MZXW6YTB")] + [InlineData("foobar", false,"MZXW6YTBOI")] + public void Encode(string? input, bool padding, string expected) + { + var actual = Base32.Encode(ToBytes(input), padding); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(null, "")] + [InlineData("", "")] + [InlineData("MY======", "f")] + [InlineData("MZXQ====", "fo")] + [InlineData("MZXW6===","foo")] + [InlineData("MZXW6YQ=", "foob")] + [InlineData("MZXW6YTB", "fooba")] + [InlineData("MZXW6YTBOI======", "foobar")] + [InlineData("MY", "f")] + [InlineData("MZXQ", "fo")] + [InlineData("MZXW6", "foo")] + [InlineData("MZXW6YQ", "foob")] + [InlineData("MZXW6YTBOI", "foobar")] + public void Decode(string? input, string expected) + { + var actual = Encoding.ASCII.GetString(Base32.Decode(input)); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(null, true,"")] + [InlineData("", true,"")] + [InlineData("f", true,"CO======")] + [InlineData("fo", true,"CPNG====")] + [InlineData("foo", true,"CPNMU===")] + [InlineData("foob", true,"CPNMUOG=")] + [InlineData("fooba", true,"CPNMUOJ1")] + [InlineData("foobar", true,"CPNMUOJ1E8======")] + [InlineData("f", false,"CO")] + [InlineData("fo", false,"CPNG")] + [InlineData("foo", false,"CPNMU")] + [InlineData("foob", false,"CPNMUOG")] + [InlineData("fooba", false,"CPNMUOJ1")] + [InlineData("foobar", false,"CPNMUOJ1E8")] + public void EncodeHex(string? input, bool padding, string expected) + { + var actual = Base32.EncodeHex(ToBytes(input), padding); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(null, "")] + [InlineData("", "")] + [InlineData("CO======", "f" )] + [InlineData("CPNG====", "fo")] + [InlineData("CPNMU===", "foo" )] + [InlineData("CPNMUOG=", "foob" )] + [InlineData("CPNMUOJ1", "fooba")] + [InlineData("CPNMUOJ1E8======", "foobar")] + [InlineData("CO", "f" )] + [InlineData("CPNG", "fo")] + [InlineData("CPNMU", "foo")] + [InlineData("CPNMUOG", "foob")] + [InlineData("CPNMUOJ1E8", "foobar")] + public void DecodeHex(string? input, string expected) + { + var actual = Encoding.ASCII.GetString(Base32.DecodeHex(input)); + Assert.Equal(expected, actual); + } +} diff --git a/Abblix.Utils.UnitTests/GlobalUsings.cs b/Abblix.Utils.UnitTests/GlobalUsings.cs new file mode 100644 index 00000000..921500a2 --- /dev/null +++ b/Abblix.Utils.UnitTests/GlobalUsings.cs @@ -0,0 +1,23 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +global using Xunit; \ No newline at end of file diff --git a/Abblix.Utils/Abblix.Utils.csproj b/Abblix.Utils/Abblix.Utils.csproj new file mode 100644 index 00000000..30dc67ce --- /dev/null +++ b/Abblix.Utils/Abblix.Utils.csproj @@ -0,0 +1,31 @@ + + + + net6.0;net7.0;net8.0 + enable + enable + true + true + Abblix.Utils + Abblix Utils + Abblix.Utils is a comprehensive utility library for .NET that enhances data manipulation and processing capabilities across applications. It includes advanced URI and string manipulation tools, cryptographic functionalities, and custom JSON converters to streamline serialization tasks. Key features include handling complex URI components, extending string operations, providing secure random data generation, and implementing custom converters for efficient JSON processing. + Abblix LLP + https://www.abblix.com/abblix-oidc-server + https://github.com/Abblix/Oidc.Server + git + Abblix Utilities String-Manipulation URI-Parsing JSON-Serialization Cryptography Data-Encoding Extensions .NET-Utilities Secure-Random Custom-JSON-Converters Base32-Encoding Asynchronous-Utilities + README.md + LICENSE.md + Copyright (c) 2024 Abblix LLP. All rights reserved. + For detailed release notes, visit: https://github.com/Abblix/Oidc.Server/releases + Abblix.png + true + + + + + + + + + diff --git a/Abblix.Utils/ArrayExtensions.cs b/Abblix.Utils/ArrayExtensions.cs new file mode 100644 index 00000000..5aaf93c4 --- /dev/null +++ b/Abblix.Utils/ArrayExtensions.cs @@ -0,0 +1,68 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Provides extension methods for arrays. +/// +public static class ArrayExtensions +{ + /// + /// Appends a value to the end of an array. + /// + /// The array to append to. + /// The value to append. + /// The type of the elements in the array. + /// A new array with the value appended. + /// + /// This method creates a new array with a size larger by one than the original array, + /// copies all elements from the original array, and adds the specified value at the end. + /// + public static T[] Append(this T[] array, T value) + { + var result = new T[array.Length + 1]; + Array.Copy(array, 0, result, 0, array.Length); + result[^1] = value; + return result; + } + + /// + /// Prepends a value to the beginning of an array. + /// + /// The array to prepend to. + /// The value to prepend. + /// The type of the elements in the array. + /// A new array with the value prepended. + /// + /// This method creates a new array with a size larger by one than the original array, + /// copies all elements from the original array starting from the second position, + /// and adds the specified value at the beginning. + /// + public static T[] Prepend(this T[] array, T value) + { + var result = new T[array.Length + 1]; + Array.Copy(array, 0, result, 1, array.Length); + result[0] = value; + return result; + } +} diff --git a/Abblix.Utils/AsyncEnumerableExtensions.cs b/Abblix.Utils/AsyncEnumerableExtensions.cs new file mode 100644 index 00000000..8d07f122 --- /dev/null +++ b/Abblix.Utils/AsyncEnumerableExtensions.cs @@ -0,0 +1,252 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Provides extension methods for working with asynchronous enumerable sequences. +/// +public static class AsyncEnumerableExtensions +{ + /// + /// Prepends a value to the beginning of an asynchronous sequence. + /// + /// The type of elements in the sequence. + /// The asynchronous sequence. + /// The value to prepend. + /// An IAsyncEnumerable sequence that starts with the specified value followed by the original sequence. + public static async IAsyncEnumerable PrependAsync(this IAsyncEnumerable values, T firstValue) + { + yield return firstValue; + + await foreach (var value in values) + { + yield return value; + } + } + + + /// + /// Appends a value to the end of an asynchronous sequence. + /// + /// The type of elements in the sequence. + /// The asynchronous sequence. + /// The value to append. + /// An IAsyncEnumerable sequence that ends with the specified value after the original sequence. + public static async IAsyncEnumerable AppendAsync(this IAsyncEnumerable values, T lastValue) + { + await foreach (var value in values) + { + yield return value; + } + + yield return lastValue; + } + + /// + /// Filters an asynchronous sequence to only include distinct elements. + /// + /// The type of elements in the sequence. + /// The asynchronous sequence. + /// An IAsyncEnumerable sequence that contains distinct elements from the original sequence. + public static async IAsyncEnumerable DistinctAsync(this IAsyncEnumerable values) + { + var uniqueValues = new HashSet(); + await foreach (var value in values) + { + if (uniqueValues.Add(value)) + yield return value; + } + } + + /// + /// Projects each element of an asynchronous sequence into a new form. + /// + /// An asynchronous sequence of values to project. + /// A transform function to apply to each element. + /// The type of elements in the source sequence. + /// The type of elements in the resulting sequence. + /// An asynchronous sequence whose elements are the result of invoking the transform function on each element of the source. + public static async IAsyncEnumerable SelectAsync(this IAsyncEnumerable values, Func selector) + { + await foreach (var value in values) + { + yield return selector(value); + } + } + + /// + /// Projects each element of an asynchronous sequence to an IEnumerable and flattens the resulting sequences into one sequence. + /// + /// An asynchronous sequence of values to project. + /// A transform function to apply to each element. + /// The type of elements in the source sequence. + /// The type of elements in the resulting sequence. + /// An asynchronous sequence whose elements are the result of invoking the transform function on each element of the source and then flattening the results. + public static async IAsyncEnumerable SelectManyAsync(this IAsyncEnumerable values, Func> selector) + { + await foreach (var valueSet in values) + foreach (var value in selector(valueSet)) + { + yield return value; + } + } + + /// + /// Projects each element of an asynchronous sequence to an IAsyncEnumerable and flattens the resulting asynchronous sequences into one sequence. + /// + /// An asynchronous sequence of values to project. + /// A transform function to apply to each element. + /// The type of elements in the source sequence. + /// The type of elements in the resulting sequence. + /// An asynchronous sequence whose elements are the result of invoking the transform function on each element of the source and then flattening the asynchronous results. + public static async IAsyncEnumerable SelectManyAsync(this IAsyncEnumerable values, Func> selector) + { + await foreach (var valueSet in values) + await foreach (var value in selector(valueSet)) + { + yield return value; + } + } + + /// + /// Filters an asynchronous sequence of values based on a predicate. + /// + /// An asynchronous sequence to filter. + /// A function to test each element for a condition. + /// The type of elements in the source sequence. + /// An asynchronous sequence that contains elements from the input sequence that satisfy the condition. + public static async IAsyncEnumerable WhereAsync(this IAsyncEnumerable values, Func condition) + { + await foreach (var value in values) + { + if (condition(value)) + yield return value; + } + } + + /// + /// Returns the first element of an asynchronous sequence, or a default value if no such element is found. + /// + /// An asynchronous sequence to return the first element of. + /// The type of elements in the source sequence. + /// A task that represents the asynchronous operation. The task result contains the first element in the sequence, + /// or the default value for the type if the sequence contains no elements. + public static async Task FirstOrDefaultAsync(this IAsyncEnumerable values) + { + await foreach (var value in values) + { + return value; + } + + return default; + } + + /// + /// Creates a List from an IAsyncEnumerable by enumerating it asynchronously. + /// + /// An asynchronous sequence to create a list from. + /// The type of elements in the source sequence. + /// A task that represents the asynchronous operation. The task result is a list that contains elements from the input sequence. + + public static async Task> ToListAsync(this IAsyncEnumerable values) + { + var result = new List(); + + await foreach (var value in values) + { + result.Add(value); + } + + return result; + } + + /// + /// Converts an IEnumerable to an IAsyncEnumerable. + /// + /// A sequence to convert to an asynchronous sequence. + /// The type of elements in the source sequence. + /// An IAsyncEnumerable that contains elements from the input sequence. + public static IAsyncEnumerable AsAsync(this IEnumerable sequence) => new AsyncEnumerable(sequence); + + private sealed class AsyncEnumerable : IAsyncEnumerable + { + public AsyncEnumerable(IEnumerable sequence) + { + _sequence = sequence; + } + + private readonly IEnumerable _sequence; + + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => new AsyncEnumerator(_sequence.GetEnumerator(), cancellationToken); + } + + /// + /// Provides an implementation of IAsyncEnumerator for enumerating over a sequence asynchronously. + /// + /// The type of elements in the collection. + private class AsyncEnumerator : IAsyncEnumerator, IDisposable + { + /// + /// Initializes a new instance of the AsyncEnumerator class. + /// + /// The enumerator of the underlying collection. + /// The token to monitor for cancellation requests. + public AsyncEnumerator(IEnumerator enumerator, CancellationToken cancellationToken) + { + _enumerator = enumerator; + _cancellationToken = cancellationToken; + } + + private readonly IEnumerator _enumerator; + private readonly CancellationToken _cancellationToken; + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + public T Current => _enumerator.Current; + + /// + /// Advances the enumerator asynchronously to the next element of the collection. + /// + /// A ValueTask that will complete with a result of true if the enumerator was successfully advanced + /// to the next element, or false if the enumerator has passed the end of the collection. + public ValueTask MoveNextAsync() + => ValueTask.FromResult(!_cancellationToken.IsCancellationRequested && _enumerator.MoveNext()); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + => _enumerator.Dispose(); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. + /// + public ValueTask DisposeAsync() + { + Dispose(); + return ValueTask.CompletedTask; + } + } +} diff --git a/Abblix.Utils/Base32.cs b/Abblix.Utils/Base32.cs new file mode 100644 index 00000000..6c39527d --- /dev/null +++ b/Abblix.Utils/Base32.cs @@ -0,0 +1,166 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text; + +namespace Abblix.Utils; + +/// +/// Provides methods for encoding and decoding data using Base32 and Base32Hex encoding schemes. +/// +public static class Base32 +{ + private const int LettersCount = 'Z' - 'A' + 1; + private const int DigitsCount = '9' - '0' + 1; + + /// + /// Encodes a byte array into a Base32 string. + /// + /// The data to encode. + /// Indicates whether to add padding characters. + /// The Base32 encoded string. + public static string Encode(byte[]? data, bool padding = true) => Encode( + data, + padding, + index => index < LettersCount ? 'A' + index : '2' + index - LettersCount); + + /// + /// Encodes a byte array into a Base32 Hex string. + /// + /// The data to encode. + /// Indicates whether to add padding characters. + /// The Base32 Hex encoded string. + public static string EncodeHex(byte[]? data, bool padding = true) => Encode( + data, + padding, + index => index < DigitsCount ? '0' + index : 'A' + index - DigitsCount); + + /// + /// Encodes a byte array into a string using the specified encoder function. + /// + /// The data to encode. + /// Indicates whether to add padding characters. + /// The function to convert a 5-bit index to a character. + /// The encoded string. + private static string Encode(IReadOnlyList? data, bool padding, Func encoder) + { + if (data == null || data.Count == 0) + return string.Empty; + + var encodedLength = (data.Count + 4) / 5 * 8; + var result = new StringBuilder(encodedLength); + + for (var i = 0; i < data.Count; i += 5) + { + var byteCount = Math.Min(5, data.Count - i); + + ulong buffer = 0; + for (var j = 0; j < byteCount; j++) + { + buffer = buffer << 8 | data[i + j]; + } + + for (var bitCount = byteCount * 8; 0 < bitCount; bitCount -= 5) + { + var index = bitCount < 5 + ? (buffer & (ulong)(0x1F >> 5 - bitCount)) << 5 - bitCount + : buffer >> bitCount - 5 & 0x1F; + + var symbol = encoder((int)index); + result.Append((char)symbol); + } + } + + if (padding) + result.Append('=', encodedLength - result.Length); + + return result.ToString(); + } + + /// + /// Decodes a Base32 encoded string into a byte array. + /// + /// The Base32 encoded string. + /// The decoded byte array. + public static byte[] Decode(string? encoded) => Decode( + encoded, + c => c switch + { + >= 'A' and <= 'Z' => c - 'A', + >= '2' and <= '7' => c - '2' + LettersCount, + _ => throw new ArgumentException($"Base32 string contains invalid character {c}"), + }); + + /// + /// Decodes a Base32 Hex encoded string into a byte array. + /// + /// The Base32 Hex encoded string. + /// The decoded byte array. + public static byte[] DecodeHex(string? encoded) => Decode( + encoded, + c => c switch + { + >= 'A' and <= 'Z' => c - 'A' + DigitsCount, + >= '0' and <= '9' => c - '0', + _ => throw new ArgumentException($"Base32 hex string contains invalid character {c}"), + }); + + /// + /// Decodes an encoded string into a byte array using the specified decoder function. + /// + /// The encoded string. + /// The function to convert a character to a 5-bit index. + /// The decoded byte array. + private static byte[] Decode(string? encoded, Func decoder) + { + if (string.IsNullOrEmpty(encoded)) + return Array.Empty(); + + var decodedLength = encoded.Length / 8 * 5; + var result = new List(decodedLength); + + for (var i = 0; i < encoded.Length; i += 8) + { + ulong buffer = 0; + var validCharsCount = 0; + for (var j = 0; j < 8; j++) + { + if (encoded.Length <= i + j) + break; + + var c = encoded[i + j]; + if (c == '=') + break; + + buffer = buffer << 5 | (uint)decoder(c); + validCharsCount++; + } + + for (var bitCount = validCharsCount * 5; 8 <= bitCount; bitCount -= 8) + { + result.Add((byte)(buffer >> bitCount - 8 & 0xFF)); + } + } + + return result.ToArray(); + } +} diff --git a/Abblix.Utils/CertificateId.cs b/Abblix.Utils/CertificateId.cs new file mode 100644 index 00000000..72072273 --- /dev/null +++ b/Abblix.Utils/CertificateId.cs @@ -0,0 +1,31 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Represents the identifiers for a certificate, including paths to certificate and key files, and an optional password. +/// +/// Path to the certificate file in PEM format. +/// Optional path to the key file in PEM format. If not provided, assumes the certificate file contains the key. +/// Optional password for the key file. Required if the key file is encrypted. +public record CertificateId(string CertPemFilePath, string? KeyPemFilePath = null, string? Password = null); diff --git a/Abblix.Utils/CryptoRandom.cs b/Abblix.Utils/CryptoRandom.cs new file mode 100644 index 00000000..76f66f1c --- /dev/null +++ b/Abblix.Utils/CryptoRandom.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography; + + + +namespace Abblix.Utils; + +/// +/// Provides cryptographic random number generation. +/// +public static class CryptoRandom +{ + /// + /// Generates a specified number of random bytes. + /// + /// The number of random bytes to generate. + /// An array of bytes filled with cryptographically strong random values. + public static byte[] GetRandomBytes(int count) + { + var buffer = new byte[count]; + using var random = RandomNumberGenerator.Create(); + random.GetBytes(buffer); + return buffer; + } +} diff --git a/Abblix.Utils/EnumerableExtensions.cs b/Abblix.Utils/EnumerableExtensions.cs new file mode 100644 index 00000000..30fe795f --- /dev/null +++ b/Abblix.Utils/EnumerableExtensions.cs @@ -0,0 +1,106 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Provides extension methods for IEnumerable<T> for common operations. +/// +public static class EnumerableExtensions +{ + /// + /// Returns the original sequence, or an empty sequence if the original is null. + /// + /// The type of elements in the sequence. + /// The sequence to return or an empty sequence if this is null. + /// The original sequence or an empty sequence. + public static IEnumerable OrEmpty(this IEnumerable? value) + => value ?? Enumerable.Empty(); + + /// + /// Traverses a hierarchy upwards, starting from a specific item and moving to its parent, grandparent, etc., as determined by a parent selector function. + /// + /// The type of elements in the hierarchy. + /// The starting item in the hierarchy. + /// A function that returns the parent of a given item. + /// An IEnumerable<T> representing the path from the item upwards in the hierarchy. + public static IEnumerable TravelUp(this T item, Func parentSelector) + { + for (var current = item; current != null; ) + { + yield return current; + + var parent = parentSelector(current); + + if (ReferenceEquals(parent, current)) + yield break; + + current = parent; + } + } + + /// + /// Flattens a tree structure into a flat sequence using breadth-first traversal. + /// + /// The type of elements in the tree. + /// The sequence of root elements. + /// A function to retrieve the children of an element. + /// A flattened sequence of all elements in the tree. + public static IEnumerable FlattenTree(this IEnumerable input, Func> childrenSelector) + { + var queue = new Queue(); + queue.EnqueueAll(input); + return queue.FlattenTreeImpl(childrenSelector); + } + + /// + /// Flattens a tree structure starting from a single root element into a flat sequence using breadth-first traversal. + /// + /// The type of elements in the tree. + /// The root element of the tree. + /// A function to retrieve the children of an element. + /// A flattened sequence of all elements in the tree. + public static IEnumerable FlattenTree(this T root, Func> childrenSelector) + { + var queue = new Queue(); + queue.Enqueue(root); + return queue.FlattenTreeImpl(childrenSelector); + } + + private static IEnumerable FlattenTreeImpl(this Queue queue, Func> childrenSelector) + { + while (0 < queue.Count) + { + var item = queue.Dequeue(); + yield return item; + queue.EnqueueAll(childrenSelector(item)); + } + } + + private static void EnqueueAll(this Queue queue, IEnumerable input) + { + foreach (var item in input.OrEmpty()) + { + queue.Enqueue(item); + } + } +} diff --git a/Abblix.Utils/HexConverter.cs b/Abblix.Utils/HexConverter.cs new file mode 100644 index 00000000..b0de7ca3 --- /dev/null +++ b/Abblix.Utils/HexConverter.cs @@ -0,0 +1,36 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Provides a static method for converting a byte array to its hexadecimal string representation. +/// +public static class HexConverter +{ + /// + /// Converts a byte array to a hexadecimal string representation. + /// + /// The byte array to convert. + /// A string representing the hexadecimal representation of the byte array. + public static string ToHexString(this byte[] bytes) => Convert.ToHexString(bytes); +} diff --git a/Abblix.Utils/HttpServerUtility.cs b/Abblix.Utils/HttpServerUtility.cs new file mode 100644 index 00000000..46b5144c --- /dev/null +++ b/Abblix.Utils/HttpServerUtility.cs @@ -0,0 +1,112 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// Provides utility methods for encoding and decoding URL tokens. +/// +public static class HttpServerUtility +{ + /// + /// Decodes a URL token into a byte array. + /// + /// The URL token to decode. + /// A byte array representing the decoded data. + /// + /// This method converts URL-safe characters ('-' and '_') back to their + /// original Base64 equivalents ('+' and '/') and then decodes the Base64 string. + /// + public static byte[] UrlTokenDecode(string input) + { + var length = input.Length; + if (length == 0) + return Array.Empty(); + + unchecked + { + var buffer = new char[(length + 3) & ~3]; + Array.Fill(buffer, '=', buffer.Length - 3, 3); + for (var i = 0; i < length; i++) + { + buffer[i] = input[i] switch + { + '-' => '+', + '_' => '/', + _ => input[i], + }; + } + return Convert.FromBase64CharArray(buffer, 0, buffer.Length); + } + } + + /// + /// Encodes a byte array into a URL token. + /// + /// The byte array to encode. + /// The number of bytes to encode. If null, encodes the entire array. + /// A URL-safe token representing the encoded data. + /// + /// Thrown when the specified length is greater than the actual length of the input array. + /// + /// + /// This method first converts the byte array to a Base64 string, then replaces Base64-specific + /// characters ('+' and '/') with URL-safe characters ('-' and '_'), and trims any trailing '=' characters. + /// + public static string UrlTokenEncode(byte[]? input, int? length = null) + { + if (input == null) + return string.Empty; + + length ??= input.Length; + if (length == 0) + return string.Empty; + + if (input.Length < length.Value) + throw new ArgumentOutOfRangeException(nameof(length), length, $"The parameters has value more than actual length of input"); + + unchecked + { + var buffer = new char[((length.Value + 2) / 3) << 2]; + var outputLength = Convert.ToBase64CharArray(input, 0, length.Value, buffer, 0); + + for (var i = 0; i < outputLength; i++) + { + switch (buffer[i]) + { + case '=': + return new string(buffer, 0, i); + + case '+': + buffer[i] = '-'; + break; + + case '/': + buffer[i] = '_'; + break; + } + } + + return new string(buffer); + } + } +} diff --git a/Abblix.Utils/ICertificateProvider.cs b/Abblix.Utils/ICertificateProvider.cs new file mode 100644 index 00000000..8d2bfd6d --- /dev/null +++ b/Abblix.Utils/ICertificateProvider.cs @@ -0,0 +1,46 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Security.Cryptography.X509Certificates; + + + +namespace Abblix.Utils; + +/// +/// Defines an interface for providing X.509 certificates. +/// +public interface ICertificateProvider +{ + /// + /// Retrieves an X.509 certificate based on the specified certificate identifier. + /// + /// The identifier of the certificate to retrieve. + /// An instance representing the requested certificate. + /// + /// Thrown if a certificate with the specified identifier is not found. + /// + /// + /// Thrown if there is an issue in retrieving the certificate, such as issues with certificate format or storage. + /// + X509Certificate2 GetCertificate(CertificateId certificateId); +} diff --git a/Abblix.Utils/Json/ArrayConverter.cs b/Abblix.Utils/Json/ArrayConverter.cs new file mode 100644 index 00000000..4b97015e --- /dev/null +++ b/Abblix.Utils/Json/ArrayConverter.cs @@ -0,0 +1,110 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A custom JSON converter that handles the serialization and deserialization of arrays of a specific type. +/// Utilizes a specified element converter for individual elements of the array. +/// +/// The type of the elements in the array. +/// The type of the converter used for the elements in the array. +public class ArrayConverter : JsonConverter + where TConverter: JsonConverter, new() +{ + private readonly TConverter _elementConverter = new(); + + /// + /// Reads and converts the JSON to an array of type . + /// + /// The reader to read JSON from. + /// The type of object to convert to. + /// Options for the serializer. + /// An array of or null if the JSON token is null. + public override TElement[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + reader.Read(); + return null; + + case JsonTokenType.StartArray: + break; + + default: + throw new JsonException(); + } + + var result = new List(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var element = _elementConverter.Read(ref reader, typeof(TElement), options); + if (element != null) + result.Add(element); + break; + + case JsonTokenType.EndArray: + return result.ToArray(); + + default: + throw new JsonException(); + } + } + + throw new JsonException(); + } + + /// + /// Writes an array of to JSON. + /// + /// The writer to write JSON to. + /// The array of to write. + /// Options for the serializer. + public override void Write(Utf8JsonWriter writer, TElement[]? value, JsonSerializerOptions options) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + writer.WriteStartArray(); + try + { + foreach (var element in value) + { + _elementConverter.Write(writer, element, options); + } + } + finally + { + writer.WriteEndArray(); + } + } +} diff --git a/Abblix.Utils/Json/Base64UrlTextEncoderConverter.cs b/Abblix.Utils/Json/Base64UrlTextEncoderConverter.cs new file mode 100644 index 00000000..f12b4aeb --- /dev/null +++ b/Abblix.Utils/Json/Base64UrlTextEncoderConverter.cs @@ -0,0 +1,64 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A JSON converter for handling the serialization and deserialization of byte arrays as Base64 URL-encoded strings. +/// +public class Base64UrlTextEncoderConverter : JsonConverter +{ + /// + /// Reads and converts the JSON token to a byte array. + /// + /// The reader to read the JSON token from. + /// The type of object to convert. + /// Options for the serializer. + /// A byte array if the token is a string, otherwise null. + /// Thrown if the token is not a string or null. + public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.String => HttpServerUtility.UrlTokenDecode(reader.GetString()!), + JsonTokenType.Null => null, + _ => throw new InvalidOperationException($"Invalid token type: {reader.TokenType}"), + }; + } + + /// + /// Writes a byte array as a Base64 URL-encoded string to the JSON writer. + /// + /// The writer to write the JSON token to. + /// The byte array to write. + /// Options for the serializer. + public override void Write(Utf8JsonWriter writer, byte[]? value, JsonSerializerOptions options) + { + if (value != null) + writer.WriteStringValue(HttpServerUtility.UrlTokenEncode(value)); + else + writer.WriteNullValue(); + } +} diff --git a/Abblix.Utils/Json/CultureInfoConverter.cs b/Abblix.Utils/Json/CultureInfoConverter.cs new file mode 100644 index 00000000..c97d0c11 --- /dev/null +++ b/Abblix.Utils/Json/CultureInfoConverter.cs @@ -0,0 +1,73 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A custom JSON converter for objects. +/// It allows for the serialization and deserialization of instances +/// when working with JSON data. +/// +public class CultureInfoConverter : JsonConverter +{ + /// + /// Reads and converts the JSON to a . + /// + /// The reader to read the JSON from. + /// The type of object to convert to. + /// The serializer options to use. + /// The deserialized object. + /// + /// Thrown when the JSON token is not a string or null, or if the string is not a valid culture identifier. + /// + public override CultureInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.Null => CultureInfo.InvariantCulture, + JsonTokenType.String => new CultureInfo(reader.GetString().NotNull("reader.GetString() != null")), + _ => throw new JsonException(), + }; + } + + /// + /// Writes a specified object as JSON. + /// + /// The writer to write the JSON to. + /// The value to convert. + /// The serializer options to use. + /// + /// If the is , + /// it writes a null value; otherwise, it writes the culture name as a string. + /// + public override void Write(Utf8JsonWriter writer, CultureInfo value, JsonSerializerOptions options) + { + if (ReferenceEquals(value, CultureInfo.InvariantCulture)) + writer.WriteNullValue(); + else + writer.WriteStringValue(value.Name); + } +} diff --git a/Abblix.Utils/Json/DateTimeOffsetUnixTimeSecondsConverter.cs b/Abblix.Utils/Json/DateTimeOffsetUnixTimeSecondsConverter.cs new file mode 100644 index 00000000..820f7573 --- /dev/null +++ b/Abblix.Utils/Json/DateTimeOffsetUnixTimeSecondsConverter.cs @@ -0,0 +1,52 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A custom JSON converter for handling objects. +/// Converts to and from Unix time, which is the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970. +/// +public class DateTimeOffsetUnixTimeSecondsConverter: JsonConverter +{ + /// + /// Reads and converts the JSON to a object. + /// + /// The reader to read JSON from. + /// The type of object to convert to. + /// Options for the serializer. + /// A object. + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); + + /// + /// Writes a object as a Unix time (number of seconds since Unix epoch) to JSON. + /// + /// The writer to write JSON to. + /// The value to write. + /// Options for the serializer. + public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) + => writer.WriteNumberValue(value.ToUnixTimeSeconds()); +} diff --git a/Abblix.Utils/Json/SpaceSeparatedValuesConverter.cs b/Abblix.Utils/Json/SpaceSeparatedValuesConverter.cs new file mode 100644 index 00000000..920d0648 --- /dev/null +++ b/Abblix.Utils/Json/SpaceSeparatedValuesConverter.cs @@ -0,0 +1,51 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A custom JSON converter that handles arrays of strings, converting them to and from space-separated values in JSON. +/// +public class SpaceSeparatedValuesConverter: JsonConverter +{ + /// + /// Reads a JSON string containing space-separated values and converts it to an array of strings. + /// + /// The reader to read JSON from. + /// The type of object to convert to. Expected to be an array of strings. + /// Options for the serializer. + /// An array of strings parsed from the space-separated values in the JSON string. + public override string[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => reader.GetString()?.Split(' '); + + /// + /// Writes an array of strings to JSON as a single string with values separated by spaces. + /// + /// The writer to write JSON to. + /// The array of strings to write. + /// Options for the serializer. + public override void Write(Utf8JsonWriter writer, string[] value, JsonSerializerOptions options) + => writer.WriteStringValue(string.Join(' ', value)); +} diff --git a/Abblix.Utils/Json/TimeSpanSecondsConverter.cs b/Abblix.Utils/Json/TimeSpanSecondsConverter.cs new file mode 100644 index 00000000..cde499f4 --- /dev/null +++ b/Abblix.Utils/Json/TimeSpanSecondsConverter.cs @@ -0,0 +1,59 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Abblix.Utils.Json; + +/// +/// A custom JSON converter for TimeSpan objects that serializes and deserializes them as a number of seconds. +/// +public class TimeSpanSecondsConverter : JsonConverter +{ + /// + /// Reads a JSON number representing the total seconds and converts it to a TimeSpan object. + /// + /// The reader to read JSON data from. + /// The type to convert; expected to be TimeSpan. + /// Options for the serializer. + /// A TimeSpan object representing the value read from the JSON. + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return TimeSpan.FromSeconds( + reader.TokenType switch + { + JsonTokenType.String when long.TryParse(reader.GetString(), out var parsed) => parsed, + JsonTokenType.Number => reader.GetInt64(), + _ => throw new JsonException(), + }); + } + + /// + /// Writes a TimeSpan object to JSON as a number representing its total seconds. + /// + /// The writer to write JSON data to. + /// The TimeSpan value to write. + /// Options for the serializer. + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + => writer.WriteNumberValue((long)value.TotalSeconds); +} diff --git a/Abblix.Utils/ObjectExtensions.cs b/Abblix.Utils/ObjectExtensions.cs new file mode 100644 index 00000000..3c4de619 --- /dev/null +++ b/Abblix.Utils/ObjectExtensions.cs @@ -0,0 +1,55 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics.CodeAnalysis; + +namespace Abblix.Utils; + +/// +/// Provides extension methods for objects to ensure non-null values. +/// +public static class ObjectExtensions +{ + /// + /// Ensures that the specified nullable reference type is not null. + /// Throws an InvalidOperationException if it is null. + /// + /// The type of the nullable reference type. + /// The nullable reference type to check for null. + /// The name of the nullable reference type, which will be used in the exception message if the value is null. + /// The original non-null value of type T. + /// Thrown when the input value is null. + public static T NotNull([NotNull] this T? value, string valueName) where T : class + => value ?? throw new InvalidOperationException($"{valueName} is expected to be not null"); + + /// + /// Ensures that the specified nullable value type is not null. + /// Throws an InvalidOperationException if it is null. + /// + /// The type of the nullable value type. + /// The nullable value type to check for null. + /// The name of the nullable value type, which will be used in the exception message if the value is null. + /// The original non-null value of type T. + /// Thrown when the input value is null. + public static T NotNull([NotNull] this T? value, string valueName) where T : struct + => value ?? throw new InvalidOperationException($"{valueName} is expected to be not null"); +} diff --git a/Abblix.Utils/ParametersBuilder.cs b/Abblix.Utils/ParametersBuilder.cs new file mode 100644 index 00000000..4838bd90 --- /dev/null +++ b/Abblix.Utils/ParametersBuilder.cs @@ -0,0 +1,67 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Collections.Specialized; +using System.Web; + + + +namespace Abblix.Utils; + +/// +/// Provides a builder for constructing and manipulating query strings or URI fragment parts. +/// +public class ParametersBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// A string representing the initial query string or URI fragment. + public ParametersBuilder(string valuesString = "") + { + _values = HttpUtility.ParseQueryString(valuesString); + } + + private readonly NameValueCollection _values; + + /// + /// Gets or sets the value associated with the specified parameter name. + /// + /// The name of the parameter to get or set. + /// The value associated with the specified name. + public string? this[string name] + { + get => _values[name]; + set => _values[name] = value; + } + + /// + /// Returns a string that represents the current query string or URI fragment. + /// + /// A string that represents the current state of the builder. + public override string ToString() => _values.ToString() ?? string.Empty; + + /// + /// Clears all the parameters from the builder. + /// + public void Clear() => _values.Clear(); +} diff --git a/Abblix.Utils/StringExtensions.cs b/Abblix.Utils/StringExtensions.cs new file mode 100644 index 00000000..cc4a4754 --- /dev/null +++ b/Abblix.Utils/StringExtensions.cs @@ -0,0 +1,79 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Abblix.Utils; + +/// +/// The class provides extension methods for enhancing the functionality and ease of use of strings. +/// +public static class StringExtensions +{ + /// + /// Inserts a specified value into the source string after a specified fragment. + /// + /// The source string where the value will be inserted. + /// The fragment after which the value will be inserted. + /// The value to insert into the source string. + /// A new string with the value inserted. + /// Thrown when the fragment is not found in the source string. + public static string InsertAfter(this string source, string fragment, string value) + { + var i = source.IndexOf(fragment, StringComparison.Ordinal); + if (i < 0) throw new InvalidOperationException($"Can't find {fragment}"); + + return source.Insert(i + fragment.Length, value); + } + + /// + /// Determines whether the specified string is neither null nor empty. + /// + /// The string to test. + /// true if the value parameter is not null or an empty string (""); otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static bool HasValue([NotNullWhen(true)] this string? value) + => !string.IsNullOrEmpty(value); + + /// + /// Trims the specified suffix from the end of the string, if it exists. + /// + /// The source string to trim. + /// The suffix to remove if it exists at the end of the source string. + /// The string without the specified suffix. + public static string TrimSuffixIfExists(this string source, string suffix) + => !string.IsNullOrEmpty(suffix) && source.EndsWith(suffix) ? source[..^suffix.Length] : source; + + /// + /// Ensures that a string is neither null nor empty, throwing an exception if it is. + /// + /// The string to validate. + /// The name of the string variable, used in the exception message. + /// The original string if it is neither null nor empty. + /// Thrown if the string is null or empty. + [DebuggerStepThrough] + public static string NotNullOrEmpty([NotNull] this string? value, string valueName) + => !string.IsNullOrEmpty(value) ? value : throw new InvalidOperationException($"{valueName} is expected to be not null or empty"); +} diff --git a/Abblix.Utils/UriBuilder.cs b/Abblix.Utils/UriBuilder.cs new file mode 100644 index 00000000..379532f2 --- /dev/null +++ b/Abblix.Utils/UriBuilder.cs @@ -0,0 +1,96 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// A wrapper around System.UriBuilder, providing enhanced functionality for URI manipulation, +/// specifically for handling query strings and fragments. +/// +public class UriBuilder +{ + /// + /// Initializes a new instance of the UriBuilder class with the specified Uri instance. + /// + /// The Uri instance to use as the base of the UriBuilder. + public UriBuilder(Uri uri) + : this(new System.UriBuilder(uri)) + { + } + + /// + /// Initializes a new instance of the UriBuilder class with the specified URI string. + /// + /// A URI string to use as the base of the UriBuilder. + public UriBuilder(string uri) + : this(new System.UriBuilder(uri)) + { + } + + /// + /// Internal constructor that initializes the UriBuilder with a System.UriBuilder instance. + /// + /// The System.UriBuilder instance to wrap. + private UriBuilder(System.UriBuilder builder) + { + _builder = builder; + Query = new ParametersBuilder(_builder.Query); + Fragment = new ParametersBuilder(_builder.Fragment); + } + + private readonly System.UriBuilder _builder; + + /// + /// Gets the ParametersBuilder for the query string. + /// + public ParametersBuilder Query { get; } + + /// + /// Gets the ParametersBuilder for the fragment part of the URI. + /// + public ParametersBuilder Fragment { get; } + + /// + /// Gets the URI constructed by the UriBuilder. + /// + public Uri Uri + { + get + { + _builder.Query = Query.ToString(); + _builder.Fragment = Fragment.ToString(); + return _builder.Uri; + } + } + + /// + /// Converts a UriBuilder instance to a Uri. + /// + /// The UriBuilder instance to convert. + public static implicit operator Uri(UriBuilder builder) => builder.Uri; + + /// + /// Converts a UriBuilder instance to a string. + /// + /// The UriBuilder instance to convert. + public static implicit operator string(UriBuilder builder) => builder.Uri.ToString(); +} diff --git a/Abblix.Utils/UriExtensions.cs b/Abblix.Utils/UriExtensions.cs new file mode 100644 index 00000000..264ba5eb --- /dev/null +++ b/Abblix.Utils/UriExtensions.cs @@ -0,0 +1,96 @@ +// Abblix OIDC Server Library +// Copyright (c) Abblix LLP. All rights reserved. +// +// DISCLAIMER: This software is provided 'as-is', without any express or implied +// warranty. Use at your own risk. Abblix LLP is not liable for any damages +// arising from the use of this software. +// +// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed +// in any form outside of the official GitHub repository at: +// https://github.com/Abblix/OIDC.Server. All development and modifications +// must occur within the official repository and are managed solely by Abblix LLP. +// +// Unauthorized use, modification, or distribution of this software is strictly +// prohibited and may be subject to legal action. +// +// For full licensing terms, please visit: +// +// https://oidc.abblix.com/license +// +// CONTACT: For license inquiries or permissions, contact Abblix LLP at +// info@abblix.com + +namespace Abblix.Utils; + +/// +/// The UriExtensions class provides extension methods for the Uri class, +/// facilitating the manipulation of query strings and fragment parts of a URI. +/// +public static class UriExtensions +{ + /// + /// Adds parameters to the query string of the given URI. + /// + /// The URI to which the query parameters will be added. + /// An array of tuples containing parameter names and values. + /// A string representing the URI with added query parameters. + public static string AddToQuery(this Uri uri, (string, string?)[] parameters) + { + var builder = new UriBuilder(uri); + builder.Query.AddNotEmptyParameters(parameters); + return builder; + } + + /// + /// Adds parameters to the fragment part of the given URI. + /// + /// The URI to which the fragment parameters will be added. + /// An array of tuples containing parameter names and values. + /// A string representing the URI with added fragment parameters. + public static string AddToFragment(this Uri uri, (string, string?)[] parameters) + { + var builder = new UriBuilder(uri); + builder.Fragment.AddNotEmptyParameters(parameters); + return builder; + } + + /// + /// Adds non-empty parameters to a ParametersBuilder instance. + /// + /// The ParametersBuilder to which the parameters will be added. + /// An array of tuples containing parameter names and values. + private static void AddNotEmptyParameters(this ParametersBuilder builder, (string, string?)[] parameters) + { + foreach (var (name, value) in parameters) + { + if (value.HasValue()) + { + builder[name] = value; + } + } + } + + /// + /// Retrieves the origin (scheme, host, and optionally port) of the given URI. + /// + /// The URI from which the origin will be extracted. + /// A string representing the origin of the URI. + public static string GetOrigin(this Uri uri) + { + var originComponents = UriComponents.Scheme | UriComponents.Host; + + if (!uri.IsDefaultPort) + { + originComponents |= UriComponents.Port; + } + + return uri.GetComponents(originComponents, UriFormat.Unescaped); + } + + /// + /// Appends a trailing slash to a URI string if it does not already end with one. + /// + /// The URI string to which a trailing slash will be appended. + /// A string representing the URI with a trailing slash. + public static string AppendTrailingSlash(this string uri) => uri.EndsWith('/') ? uri : uri + "/"; +} diff --git a/Abblix.png b/Abblix.png new file mode 100644 index 00000000..1e023d16 Binary files /dev/null and b/Abblix.png differ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..0b0b3b16 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,104 @@ +# License Agreement + +This License Agreement ("Agreement") is a legal agreement between you (as a person or entity, "You") and Abblix Limited Liability Partnership ("Copyright Holder") for the OIDC Server ("Software"). + +**ATTENTION!** Please, thoroughly examine the terms and conditions in this License Agreement before operating the Software. By using the Software, you wholeheartedly and unconditionally agree to the terms set forth in this License Agreement. If any of the terms within this License Agreement are unsuitable, you have no right to use the Software and must promptly uninstall it from your system. + +## 1. Definitions +1.1. "Software" refers to the "OIDC Server" software, including any accompanying materials, updates, and extensions, the copyright of which belongs to Abblix Limited Liability Partnership. + +1.2. "System" refers to an operating system, virtual machine, or equipment, including a server, on which the Software is installed and/or used. + +1.3. "User" or "You" refers to a natural or legal person who installs and/or uses the Software on their behalf or legally owns a copy of the Software. If the Software was downloaded or acquired on behalf of a legal entity, the term "User" or "You" refers to the legal entity for which the Software was downloaded or acquired, and is accepting this Agreement through an authorized representative. + +1.4. "Partners" refers to organizations that distribute the Software based on agreement with the Copyright Holder. + +1.5. "Software Extensions" are additional software components and software solutions provided by the Copyright Holder that extend the functionality of the Software and may require the purchase of a separate license or an extension of an existing license. Software Extensions can be provided both free of charge and paid. You can obtain more detailed information before receiving such extensions. + +## 2. License Grant +2.1. You are granted a non-exclusive license to use the Software within the scope of the functionality described on the Copyright Holder's official website, available at [oidc.abblix.com/functionality](https://oidc.abblix.com/functionality), provided that you comply with all restrictions and conditions specified in this License Agreement. This license does not grant sublicensing or redistribution rights to third parties. To obtain sublicensing or redistribution rights, you must purchase a separate type of license. + +2.2. You acknowledge that the license granted under clause 2.1 does not include the right to: + - Distribute, sell, or rent; + - License, modify, reproduce, translate, adapt, reverse engineer, decompile, or disassemble any part of the Software; + - Alter the source code of any part of the Software; + - Remove, obscure, interfere with, or circumvent any feature of the Software, including, but not limited to, copyright or other intellectual property notices, security, or access control mechanisms. + +2.3. You may not use the Software in commercial projects, except as provided in clause 2.5. If you wish to use the Software for non-commercial purposes, you may download and access the Software free of charge, subject to all license terms. Examples of non-commercial projects include: + - Free educational projects; + - Games without monetization; + - Test versions of commercial systems for piloting/demonstrating performance in internal non-commercial environments without generating profit. +In the event that your product uses any types of advertising, paid subscriptions, or any type of commercial component, this software does not permit you to use it on a free basis. + +2.4. If the laws of your country prohibit you from using the Software, you are not authorized to use it, and you agree to comply with all applicable laws and regulations concerning your use of the Software. + +2.5. If you wish to use the Software in commercial projects, or if your projects have a commercial component in any way, you may download and use the Software during the term upon payment of the applicable license fee, in accordance with the terms of this Agreement. + +## 3. Activation and Duration +3.1. When installing the Software, the period of use of the Software is indicated at the time of purchase or upon receipt of the Software free of charge under certain conditions in accordance with Section 2 of this agreement. + +3.2. If you obtain the Software from a Partner, the useful life of the Software may be determined between you and the Partner. + +3.3. The Software can only be installed on platforms suitable for its use. The Copyright Holder does not provide support for the following situations: copies of the Software installed on platforms not specified on the official website page, available at [oidc.abblix.com/requirements](https://oidc.abblix.com/requirements); support requests not related to the normal use of the Software; or support requests arising from the use of third-party products that either prohibit or do not function with the Software. + +3.4. The license period for the Software can be verified on the official website page at [oidc.abblix.com/license-check](https://oidc.abblix.com/license-check). + +3.5. If you violate any of the terms of this License Agreement, the Copyright Holder has the right to terminate this License Agreement for the use of the Software immediately or by notifying you 30 calendar days in advance, depending on the severity of the violation. This advance notice is intended to ensure the continuity of your processes and give you the opportunity to correct any actual or alleged misuse or abuse of the Software or any material breach of this Agreement. + +3.6. To verify the legality of using the Software, the Copyright Holder reserves the right to use means to verify that you have a licensed copy of the Software. + +## 4. Additional Terms for Using the Software +4.1. You agree that the Software may be used by you only in accordance with its intended use and must not violate local laws. + +4.2. Your e-mail address and other data may be transferred to and further processed by a trusted third-party information system provider of the Copyright Holder. The third-party information system provider may process this data in countries where the level of protection of personal data is lower than the level of protection of personal data in your country of residence. + +4.3. This Agreement shall be in effect for the period specified in the license issued to you upon payment of the applicable license fee for the Software. The Copyright Holder may terminate this Agreement in accordance with the conditions set forth in clause 3.6. You may also terminate this Agreement for any reason by ceasing all use of the Software. Upon termination of this Agreement, no refund will be provided to you, in whole or in part, and you must immediately stop using the Software and provide evidence of such termination to the Copyright Holder upon request. + +## 5. Software Rights +5.1. The Software is wholly owned by the Copyright Holder and is licensed to You, not sold. The Software is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. Except for the limited rights of use granted herein, all rights, title, and interest in the Software, including patents, copyrights, and trademarks in and to the Software, accompanying printed materials, and any copies of the Software, belong to the Licensor. + +## 6. Receiving Information and Promotional Materials +6.1. You agree to receive informational and promotional messages from the Copyright Holder and its Partners (information about updates, new functionality, license expiration). + +## 7. Restrictions +7.1. You may not rent, lease, or lend the Software. + +7.2. For violation of intellectual rights to the software, the violator bears civil, administrative, or criminal liability in accordance with the law. + +## 8. Limited Warranty and Disclaimer of Warranties +8.1. The Copyright Holder guarantees that the Software will operate in accordance with the features described on the official website page, available at [oidc.abblix.com/functionality](https://oidc.abblix.com/functionality), provided that the latest releases of the supported versions are used, unless otherwise stipulated by the License Agreement. A list of supported versions is available on the official website page, available at [oidc.abblix.com/versions](https://oidc.abblix.com/versions). + +8.2. The Licensor will not provide you with any individual or specialized support services under this agreement. + +8.3. An Extended Support Agreement may be purchased separately from the Licensor for individual or specialized support services, offering higher service levels than those provided herein. + +8.4. You agree that no software is error-free. + +8.5. The Copyright Holder does not guarantee the functionality of the Software in the event that the User violates the terms of this License Agreement. + +8.6. The Copyright Holder does not guarantee the functionality of the Software if the User has not properly installed it. + +8.7. The Copyright Holder does not guarantee the availability of the functionality described on the official website page, available at [oidc.abblix.com/functionality](https://oidc.abblix.com/functionality), after the expiration of the period of use as described in subsection 3 of this License Agreement. + +8.8. Except for the limited warranty set forth in this paragraph, the software is provided "as is". The Copyright Holder and its partners do not provide any guarantees for its use or performance. Except for warranties, conditions, representations, or provisions that cannot be excluded or limited under applicable law, the Copyright Holder and its partners make no warranties, conditions, representations, or provisions (whether express or implied) for anything, including, without limitation, non-infringement of third-party rights, merchantability, integration, or fitness for a particular purpose. You agree that you are responsible for selecting the software to achieve the desired results, for installing and using the software, and for the results obtained with it. + +## 9. Disclaimer +9.1. To the maximum extent permitted by applicable law, the Copyright Holder and/or its partners shall not be liable for any loss and/or damage (including losses due to lost commercial profits, business interruption, loss of information, or other property damage) arising from or in connection with the use or inability to use the software, even if the Copyright Holder and its partners have been notified of the possible occurrence of such losses and/or damage. In any event, the liability of the Copyright Holder and its partners under any provision of this License Agreement is limited to the amount of $5. These limitations cannot be excluded or limited under applicable law. + +## 10. Intellectual Property Rights and Privacy +10.1. You agree that the Software, documentation, and all other objects of copyright, systems, ideas, methods of work, and other information contained in the Software, as well as trademarks, are the intellectual property of the Copyright Holder or its Partners. This License Agreement does not grant you any rights to use intellectual property, including trademarks and service marks of the Licensor or its Partners, except for the rights granted by this License Agreement. + +10.2. You agree that you will not modify or change the Software in any way. You may not remove or modify any copyright or other proprietary notices on any copy of the Software. + +10.3. The Software is confidential and proprietary information. You agree to maintain the confidentiality of the Software and not to disclose any confidential information related to the Software to any third party without the prior written consent of the Copyright Holder. + +## 11. Governing Law and Dispute Resolution +11.1. This License Agreement is governed by the laws of the Republic of Kazakhstan. However, should any disagreements or disputes arise between the parties, they may mutually agree to resolve such disputes either under the jurisdiction of the courts of the Republic of Kazakhstan or the Astana International Financial Center (AIFC), the latter of which operates under English law. Both parties explicitly consent to the chosen jurisdiction. + +11.2. In the event of any dispute, controversy, or claim arising out of or relating to this License Agreement, or the breach, termination, or invalidity thereof, the parties shall first seek to resolve the dispute through good faith negotiations. This process should involve direct communication between the parties or their designated representatives with the aim to reach an amicable settlement. If the dispute cannot be resolved, then either party may proceed to litigation as described in section 11.1. + +11.3. If any provision of this License Agreement is held to be void, voidable, unenforceable, or illegal, the remaining provisions of this License Agreement will remain in full force and effect. In the event of a conflict between the terms of this agreement and the terms of any software product license agreement concluded between you and the Partners or the Copyright Holder, the terms of such a license agreement shall prevail; in all other respects, the terms of this agreement and such agreement shall apply. + +## 12. Contact Information of the Copyright Holder +Website: [www.abblix.com](https://www.abblix.com) +Email: [info@abblix.com](mailto:info@abblix.com) diff --git a/Nuget/README.md b/Nuget/README.md new file mode 100644 index 00000000..6a87638d --- /dev/null +++ b/Nuget/README.md @@ -0,0 +1,69 @@ +# Abblix OIDC Server + +**Abblix OIDC Server** is a robust .NET library that implements the OpenID Connect protocol on the server side. It is designed to meet high standards of flexibility, reusability, and reliability, using well-known software design patterns such as modular and hexagonal architectures. These patterns ensure that different parts of the library can work independently, improving the library's modularity, testability, and maintainability. The library also supports Dependency Injection using the standard .NET DI container, which aids in better organization and management of code. Specifically tailored for seamless integration with ASP.NET WebApi, Abblix OIDC Server employs standard controller classes, binding, and routing mechanisms to simplify the integration of OpenID Connect into your services. + +## NuGet Packages Description + +- **Abblix.OIDC.Server** + This core package implements the OpenID Connect (OIDC) server functionality, providing a robust, compliant, and extensible framework for adding OIDC-based authentication and authorization to .NET applications. It supports various OIDC flows and configurations, tailored for modern application security needs. + +- **Abblix.OIDC.Server.MVC** + Tailored for ASP.NET MVC applications, this package extends the Abblix.OIDC.Server to integrate smoothly with the MVC framework. It simplifies the process of securing MVC applications with OIDC, handling the intricacies of user authentication, session management, and secure redirections. + +- **Abblix.JWT** + The Abblix.JWT package facilitates handling of JSON Web Tokens (JWTs) within the .NET ecosystem. It builds on top of `System.IdentityModel.Tokens.Jwt` to provide utilities for token validation, generation, and management, making it essential for securing web applications and services that rely on stateless authentication mechanisms. + +- **Abblix.DependencyInjection** + This package extends Microsoft's default dependency injection (DI) framework. It allows for more advanced scenarios such as overriding dependencies directly via type, instance, or factory, aliasing services, and more complex service compositions and decorations. It integrates seamlessly, enhancing flexibility and maintainability of DI configurations in .NET applications. + +- **Abblix.Utils** + A utility package that provides common functionalities needed across various parts of the Abblix OIDC server implementation. These include helpers for logging, data manipulation, and other cross-cutting concerns that are essential for the operation and maintenance of security-focused services. + +## Implemented technologies and standards + +Abblix OIDC Server fully implements a comprehensive suite of advanced standards for authorization and security, providing a robust and secure environment for authorization data handling. Here are the key standards implemented in our product. + +- **The OAuth 2.0 Authorization Framework**: [RFC 6749](https://tools.ietf.org/html/rfc6749): Defines procedures for secure authorization of applications. +- **The OAuth 2.0 Authorization Framework: Bearer Token Usage**: [RFC 6750](https://tools.ietf.org/html/rfc6750): Explains how to securely use bearer tokens to access resources. +- **OAuth 2.0 Token Revocation**: [RFC 7009](https://tools.ietf.org/html/rfc7009): Describes methods to securely cancel access and refresh tokens. +- **JSON Web Token (JWT)**: [RFC 7519](https://tools.ietf.org/html/rfc7519): Defines structure and use of JWTs for representing claims securely. +- **JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants**: [RFC 7523](https://tools.ietf.org/html/rfc7523): Uses JWTs for secure client authentication and as authorization grants. +- **Proof Key for Code Exchange by OAuth Public Clients**: [RFC 7636](https://tools.ietf.org/html/rfc7636): Improves security for public clients during authorization code exchange. +- **OAuth 2.0 Token Introspection**: [RFC 7662](https://tools.ietf.org/html/rfc7662): Allows resource servers to verify the active state and metadata of tokens. +- **OAuth 2.0 Dynamic Client Registration Protocol**: [RFC 7591](https://tools.ietf.org/html/rfc7591): Provides mechanisms for clients to register dynamically with authorization servers. +- **OAuth 2.0 Token Exchange**: [RFC 8693](https://tools.ietf.org/html/rfc8693): Details the method for a secure exchange of one token type for another. +- **JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens**: [RFC 9068](https://tools.ietf.org/html/rfc9068): Specifies the use of JWTs as OAuth 2.0 access tokens. +- **The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)**: [RFC 9101](https://tools.ietf.org/html/rfc9101): Secures authorization requests using JWTs. +- **OAuth 2.0 Pushed Authorization Requests**: [RFC 9126](https://tools.ietf.org/html/rfc9126): Enhances security by allowing clients to push authorization requests directly to the server. +- **OAuth 2.0 Authorization Server Issuer Identification**: [RFC 9207](https://tools.ietf.org/html/rfc9207): Ensures the authenticity of authorization servers to clients. +- **OpenID Connect Core**: [Core Specification](https://openid.net/specs/openid-connect-core-1_0.html): Core functionality for OpenID Connect identity layer over OAuth 2.0. +- **OpenID Connect Discovery**: [Detailed Specification](https://openid.net/specs/openid-connect-discovery-1_0.html): Enables clients to discover provider configurations dynamically. +- **OpenID Connect RP-Initiated Logout**: [Detailed Specification](https://openid.net/specs/openid-connect-rpinitiated-1_0.html): Details logout initiated by relying parties. +- **OpenID Connect Session Management**: [Detailed Specification](https://openid.net/specs/openid-connect-session-1_0.html): Manages user session states in identity providers. +- **OpenID Connect Front-Channel Logout**: [Detailed Specification](https://openid.net/specs/openid-connect-frontchannel-1_0.html): Handles logout requests through front-channel communication. +- **OpenID Connect Back-Channel Logout**: [Detailed Specification](https://openid.net/specs/openid-connect-backchannel-1_0.html): Manages logout processes using back-channel communication. +- **OAuth 2.0 Multiple Response Type Encoding Practices**: [Core Specification](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html): Encodes different response types in OAuth 2.0 requests. +- **OAuth 2.0 Form Post Response Mode**: [Core Specification](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html): Transmits OAuth 2.0 responses via HTTP form posts. +- **OpenID Connect Dynamic Client Registration**: [Detailed Specification](https://openid.net/specs/openid-connect-registration-1_0.html): Enables OpenID Connect clients to register dynamically with providers. +- **OpenID Connect Core: Pairwise Pseudonymous Identifiers (PPID)**: [Core Specification](https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg): Implements a privacy mechanism by generating unique identifiers for clients. + +## Getting Started + +To better understand the Abblix OIDC Server product, we strongly recommend visiting our comprehensive [Documentation](https://docs.abblix.com/docs) site. Please explore the [Getting Started Guide](https://docs.abblix.com/docs/getting-started-guide), designed to provide you with all the necessary instructions and tips for a thorough understanding of our solution. + +## Use our custom ChatGPT "Abblix OIDC Server Helper" + +The **Abblix OIDC Server Helper** is a specialized ChatGPT designed to assist users and developers working with the Abblix OIDC Server. This AI-powered tool provides guidance, answers questions, and offers troubleshooting help regarding the OIDC Server implementation. + +Explore the capabilities of this assistant by visiting the [Abblix OIDC Server Helper](https://chat.openai.com/g/g-1icXaNyOR-abblix-oidc-server-helper). Whether you're a new user trying to understand the basics or an experienced developer looking for specific technical details, this tool is here to help enhance your workflow and knowledge. + +For more detailed interactions and to explore its full potential, access the assistant directly through the provided link. + +## Contacts + +For more details about our products, services, or any general information regarding the Abblix OIDC Server, feel free to reach out to us. We are here to provide support and answer any questions you may have. Below are the best ways to contact our team: + +- **Email**: Send us your inquiries or support requests at [support@abblix.com](mailto:support@abblix.com). +- **Website**: Visit the official Abblix OIDC Server page for more information: [Abblix OIDC Server](https://www.abblix.com/abblix-oidc-server). + +We look forward to assisting you and ensuring your experience with our products is successful and enjoyable! diff --git a/README.md b/README.md new file mode 100644 index 00000000..072f5bb6 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +## Description +**Abblix OIDC Server** is a robust .NET library that implements the OpenID Connect protocol on the server side. It is designed to meet high standards of flexibility, reusability, and reliability, using well-known software design patterns such as modular and hexagonal architectures. These patterns ensure that different parts of the library can work independently, improving the library's modularity, testability, and maintainability. The library also supports Dependency Injection using the standard .NET DI container, which aids in better organization and management of code. Specifically tailored for seamless integration with ASP.NET WebApi, Abblix OIDC Server employs standard controller classes, binding, and routing mechanisms to simplify the integration of OpenID Connect into your services. + +## How to Build +```shell +# Open a terminal (Command Prompt or PowerShell for Windows, Terminal for macOS or Linux) + +# Ensure Git is installed +# Visit https://git-scm.com to download and install console Git if not already installed + +# Clone the repository +git clone https://github.com/Abblix/Oidc.Server.git + +# Navigate to the project directory +cd Oidc.Server + +# Check if .NET SDK is installed +dotnet --version # Check the installed version of .NET SDK +# Visit the official Microsoft website to install or update it if necessary + +# Restore dependencies +dotnet restore + +# Compile the project +dotnet build +``` + +## Getting Started + +To better understand the Abblix OIDC Server product, we strongly recommend visiting our comprehensive [Documentation](https://docs.abblix.com/docs) site. Please explore the [Getting Started Guide](https://docs.abblix.com/docs/getting-started-guide), designed to provide you with all the necessary instructions and tips for a thorough understanding of our solution. + +## Use our custom ChatGPT "Abblix OIDC Server Helper" + +The **Abblix OIDC Server Helper** is a specialized ChatGPT designed to assist users and developers working with the Abblix OIDC Server. This AI-powered tool provides guidance, answers questions, and offers troubleshooting help regarding the OIDC Server implementation. + +Explore the capabilities of this assistant by visiting the [Abblix OIDC Server Helper](https://chat.openai.com/g/g-1icXaNyOR-abblix-oidc-server-helper). Whether you're a new user trying to understand the basics or an experienced developer looking for specific technical details, this tool is here to help enhance your workflow and knowledge. + +For more detailed interactions and to explore its full potential, access the assistant directly through the provided link. + +## Feedback and Contributions + +We've made every effort to implement all the main aspects of the OpenID protocol in the best possible way. However, the development journey doesn't end here, and your input is crucial for our continuous improvement. + +> [!IMPORTANT] +> Whether you have feedback on features, have encountered any bugs, or have suggestions for enhancements, we're eager to hear from you. Your insights help us make the Abblix OIDC Server library more robust and user-friendly. + +Please feel free to contribute by [submitting an issue](https://github.com/Abblix/Oidc.Server/issues) or [joining the discussions](https://github.com/orgs/Abblix/discussions). Each contribution helps us grow and improve. + +We appreciate your support and look forward to making our product even better with your help! + +## Contacts + +For more details about our products, services, or any general information regarding the Abblix OIDC Server, feel free to reach out to us. We are here to provide support and answer any questions you may have. Below are the best ways to contact our team: + +- **Email**: Send us your inquiries or support requests at [support@abblix.com](mailto:support@abblix.com). +- **Website**: Visit the official Abblix OIDC Server page for more information: [Abblix OIDC Server](https://www.abblix.com/abblix-oidc-server). + +We look forward to assisting you and ensuring your experience with our products is successful and enjoyable! diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..5e4faeda --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Reporting Security Issues + +## Introduction + +The security of our systems is our top priority. If you have discovered a security vulnerability in our project, we appreciate your cooperation in responsibly disclosing it to us. + +## Please Follow These Steps: + +1. **Do Not Report Security Vulnerabilities Publicly** + - Please **do not** disclose any details about the vulnerability in public forums, GitHub issues, or any other public channels. Such actions can potentially lead to misuse and could harm our users. + +2. **Email Us Directly** + - To report a security issue, please send an email directly to us at [support@abblix.com](mailto:support@abblix.com). Include as much information as possible about the vulnerability, including: + - The conditions on which the vulnerability can be reproduced. + - Any potential impacts of the vulnerability. + - How you found the vulnerability, if you'd like to share this. + +## What Will Happen Next? + +- **Acknowledgement**: We will acknowledge your email within 24 hours. +- **Investigation**: Our security team will investigate the issue and work to quickly address it. +- **Communication**: We will keep you informed of our progress as we work to resolve the vulnerability. +- **Disclosure**: Once the issue has been resolved, we will work with you to coordinate the disclosure of the vulnerability to ensure that all our users are adequately informed and protected. + +## Our Commitment + +We are committed to working with security researchers and the community to make our products safer. We appreciate your efforts in responsibly reporting any issues you find. We promise to handle your report with confidentiality and will not take any legal action against you as long as you adhere to these guidelines. + +Thank you for helping us keep our products and our users safe!