generated from insight-architectures/dotnet-library-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add anonymous user middleware package (#2)
- Loading branch information
Showing
15 changed files
with
534 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
// Use IntelliSense to find out which attributes exist for C# debugging | ||
// Use hover for the description of the existing attributes | ||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md | ||
"name": ".NET Core Launch (console)", | ||
"type": "coreclr", | ||
"request": "launch", | ||
"preLaunchTask": "build", | ||
// If you have changed target frameworks, make sure to update the program path. | ||
"program": "${workspaceFolder}/tests/AnonymousUserTests/bin/Debug/net6.0/AnonymousUserTests.dll", | ||
"args": [], | ||
"cwd": "${workspaceFolder}/tests/AnonymousUserTests", | ||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console | ||
"console": "internalConsole", | ||
"stopAtEntry": false | ||
}, | ||
{ | ||
"name": ".NET Core Attach", | ||
"type": "coreclr", | ||
"request": "attach" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"version": "2.0.0", | ||
"tasks": [ | ||
{ | ||
"label": "build", | ||
"command": "dotnet", | ||
"type": "process", | ||
"args": [ | ||
"build", | ||
"${workspaceFolder}/tests/AnonymousUserTests/AnonymousUserTests.csproj", | ||
"/property:GenerateFullPaths=true", | ||
"/consoleloggerparameters:NoSummary" | ||
], | ||
"problemMatcher": "$msCompile" | ||
}, | ||
{ | ||
"label": "publish", | ||
"command": "dotnet", | ||
"type": "process", | ||
"args": [ | ||
"publish", | ||
"${workspaceFolder}/tests/AnonymousUserTests/AnonymousUserTests.csproj", | ||
"/property:GenerateFullPaths=true", | ||
"/consoleloggerparameters:NoSummary" | ||
], | ||
"problemMatcher": "$msCompile" | ||
}, | ||
{ | ||
"label": "watch", | ||
"command": "dotnet", | ||
"type": "process", | ||
"args": [ | ||
"watch", | ||
"run", | ||
"--project", | ||
"${workspaceFolder}/tests/AnonymousUserTests/AnonymousUserTests.csproj" | ||
], | ||
"problemMatcher": "$msCompile" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System; | ||
using InsightArchitectures.Extensions.AspNetCore.AnonymousUser; | ||
|
||
namespace Microsoft.AspNetCore.Builder | ||
{ | ||
/// <summary> | ||
/// Extension methods for the ASP NET Core application builder. | ||
/// </summary> | ||
public static class AnonymousUserExtensions | ||
{ | ||
/// <summary> | ||
/// Adds the <see cref="AnonymousUserMiddleware" /> to the middleware pipeline. | ||
/// </summary> | ||
/// <param name="builder">The application builder object.</param> | ||
/// <param name="configure">An action to customise the middleware options.</param> | ||
public static IApplicationBuilder UseAnonymousUser(this IApplicationBuilder builder, Action<AnonymousUserOptions> configure = null) | ||
{ | ||
var options = new AnonymousUserOptions(); | ||
|
||
configure?.Invoke(options); | ||
|
||
_ = options.ClaimType ?? throw new NullReferenceException($"{nameof(options.ClaimType)} is null. Please provide a claim type name when configuring the middleware."); | ||
|
||
return builder.UseMiddleware<AnonymousUserMiddleware>(options); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using System; | ||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace InsightArchitectures.Extensions.AspNetCore.AnonymousUser | ||
{ | ||
/// <summary> | ||
/// The anonymous user middleware. It either creates a new or reads an existing cookie | ||
/// and maps the value to a claim. | ||
/// </summary> | ||
public class AnonymousUserMiddleware | ||
{ | ||
private RequestDelegate _nextDelegate; | ||
private AnonymousUserOptions _options; | ||
|
||
/// <summary> | ||
/// Constructor requires the next delegate and options. | ||
/// </summary> | ||
public AnonymousUserMiddleware(RequestDelegate nextDelegate, AnonymousUserOptions options) | ||
{ | ||
_nextDelegate = nextDelegate ?? throw new ArgumentNullException(nameof(nextDelegate)); | ||
_options = options ?? throw new ArgumentNullException(nameof(options)); | ||
} | ||
|
||
private async Task HandleRequestAsync(HttpContext httpContext) | ||
{ | ||
var cookieEncoder = _options.EncoderService ?? throw new ArgumentNullException(nameof(_options.EncoderService), $"{nameof(_options.EncoderService)} is null and should have a valid encoder."); | ||
_ = _options.UserIdentifierFactory ?? throw new ArgumentNullException(nameof(_options.UserIdentifierFactory), $"{nameof(_options.UserIdentifierFactory)} is null and should have a valid factory."); | ||
|
||
if (httpContext.User.Identity?.IsAuthenticated == true) | ||
{ | ||
return; | ||
} | ||
|
||
var encodedValue = httpContext.Request.Cookies[_options.CookieName]; | ||
|
||
if (_options.Secure && !httpContext.Request.IsHttps) | ||
{ | ||
if (!string.IsNullOrWhiteSpace(encodedValue)) | ||
{ | ||
httpContext.Response.Cookies.Delete(_options.CookieName); | ||
} | ||
|
||
return; | ||
} | ||
|
||
var uid = await cookieEncoder.DecodeAsync(encodedValue); | ||
|
||
if (string.IsNullOrWhiteSpace(uid)) | ||
{ | ||
uid = _options.UserIdentifierFactory.Invoke(httpContext); | ||
var encodedUid = await cookieEncoder.EncodeAsync(uid); | ||
|
||
var cookieOptions = new CookieOptions | ||
{ | ||
Expires = _options.Expires, | ||
}; | ||
|
||
httpContext.Response.Cookies.Append(_options.CookieName, encodedUid, cookieOptions); | ||
} | ||
|
||
var identity = new ClaimsIdentity(new[] { new Claim(_options.ClaimType, uid) }); | ||
httpContext.User.AddIdentity(identity); | ||
} | ||
|
||
/// <summary> | ||
/// Called by the pipeline, runs the handler. | ||
/// </summary> | ||
public async Task InvokeAsync(HttpContext httpContext) | ||
{ | ||
_ = httpContext ?? throw new ArgumentNullException(nameof(httpContext)); | ||
await HandleRequestAsync(httpContext); | ||
|
||
await _nextDelegate.Invoke(httpContext); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace InsightArchitectures.Extensions.AspNetCore.AnonymousUser | ||
{ | ||
/// <summary> | ||
/// Configuration options for the middleware. | ||
/// </summary> | ||
public class AnonymousUserOptions | ||
{ | ||
/// <summary>The name of the cookie.</summary> | ||
public string CookieName { get; set; } = "tid"; | ||
|
||
/// <summary>The expiration date of the cookie. Default set to 10 years.</summary> | ||
public DateTimeOffset Expires { get; set; } = DateTimeOffset.UtcNow.AddDays(3652); | ||
|
||
/// <summary>The type name of the claim holding the ID.</summary> | ||
public string ClaimType { get; set; } = "ExternalId"; | ||
|
||
/// <summary>Should the cookie only be allowed on https requests.</summary> | ||
public bool Secure { get; set; } | ||
|
||
/// <summary>Can be overridden to customise the ID generation.</summary> | ||
public Func<HttpContext, string> UserIdentifierFactory { get; set; } = _ => Guid.NewGuid().ToString(); | ||
|
||
/// <summary>The encoder service to encode/decode the cookie value. Default set to internal base64 encoder.</summary> | ||
public ICookieEncoder EncoderService { get; set; } = new Base64CookieEncoder(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace InsightArchitectures.Extensions.AspNetCore.AnonymousUser | ||
{ | ||
/// <summary> | ||
/// Default cookie value encoder/decoder. Uses base64 for serialisation. | ||
/// </summary> | ||
public class Base64CookieEncoder : ICookieEncoder | ||
{ | ||
/// <summary> | ||
/// Deserialises a base64 value into clear text. | ||
/// <param name="encodedValue">A base64 encoded value.</param> | ||
/// <returns>Returns null if <paramref name="encodedValue" /> is null, otherwise the decoded value.</returns> | ||
/// </summary> | ||
public Task<string> DecodeAsync(string encodedValue) | ||
{ | ||
if (string.IsNullOrWhiteSpace(encodedValue)) | ||
{ | ||
return Task.FromResult((string)null); | ||
} | ||
|
||
var bytes = Convert.FromBase64String(encodedValue); | ||
|
||
return Task.FromResult(Encoding.UTF8.GetString(bytes)); | ||
} | ||
|
||
/// <summary> | ||
/// Serialiases a clear text value into base64. | ||
/// <param name="value">A clear text value.</param> | ||
/// <returns>Returns null if <paramref name="value" /> is null, otherwise the encoded value.</returns> | ||
/// </summary> | ||
public Task<string> EncodeAsync(string value) | ||
{ | ||
if (string.IsNullOrWhiteSpace(value)) | ||
{ | ||
return Task.FromResult((string)null); | ||
} | ||
|
||
var bytes = Encoding.UTF8.GetBytes(value); | ||
|
||
return Task.FromResult(Convert.ToBase64String(bytes)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System.Threading.Tasks; | ||
|
||
namespace InsightArchitectures.Extensions.AspNetCore.AnonymousUser | ||
{ | ||
/// <summary> | ||
/// Interface for cookie value serialisation. | ||
/// </summary> | ||
public interface ICookieEncoder | ||
{ | ||
/// <summary> | ||
/// Serialiases a clear text value. | ||
/// <param name="value">A clear text value</param> | ||
/// </summary> | ||
Task<string> EncodeAsync(string value); | ||
|
||
/// <summary> | ||
/// Deserialises the given value into clear text. | ||
/// <param name="encodedValue">The serialised value</param> | ||
/// </summary> | ||
Task<string> DecodeAsync(string encodedValue); | ||
} | ||
} |
Oops, something went wrong.