From 218149ac8e0597dfe9c49c183b73489c2ba2b5c1 Mon Sep 17 00:00:00 2001 From: ksemenenko Date: Sun, 2 Nov 2025 20:31:40 +0100 Subject: [PATCH 1/2] Update workflows and dependencies for .NET 9 rollout --- .github/workflows/ci.yml | 40 +++ .github/workflows/codeql-analysis.yml | 58 ++--- .github/workflows/dotnet.yml | 54 ----- .github/workflows/nuget.yml | 37 --- .github/workflows/release.yml | 227 ++++++++++++++++++ Directory.Build.props | 2 +- ...ManagedCode.Orleans.Identity.Client.csproj | 8 +- .../ManagedCode.Orleans.Identity.Core.csproj | 6 +- ...ManagedCode.Orleans.Identity.Server.csproj | 10 +- .../ManagedCode.Orleans.Identity.Tests.csproj | 26 +- README.md | 128 +++++----- 11 files changed, 377 insertions(+), 219 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/dotnet.yml delete mode 100644 .github/workflows/nuget.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..925d975 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +env: + DOTNET_VERSION: '9.0.x' + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage" + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./**/coverage.cobertura.xml + fail_ci_if_error: false diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7ae370e..a6ae2da 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,67 +1,49 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" on: push: branches: [ main ] pull_request: - # The branches below must be a subset of the branches above branches: [ main ] schedule: - - cron: '35 11 * * 4' + - cron: '0 0 * * 1' + +env: + DOTNET_VERSION: '9.0.x' jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write strategy: fail-fast: false matrix: language: [ 'csharp' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # โ„น๏ธ Command-line programs to run using the OS shell. - # ๐Ÿ“š https://git.io/JvXDl - # โœ๏ธ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + - name: Restore dependencies + run: dotnet restore - #- run: | - # make bootstrap - # make release + - name: Build + run: dotnet build --no-restore - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml deleted file mode 100644 index 0b98ce0..0000000 --- a/.github/workflows/dotnet.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: .NET - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - - build-and-test: - runs-on: ubuntu-latest - - steps: - - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 9.0.x - - # run build and test - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --no-restore - - - name: Test and Collect Code Coverage - run: dotnet test -p:CollectCoverage=true -p:CoverletOutput=coverage/ - - - name: Copy coverage files - run: | - mkdir '${{ github.workspace }}/coverage' - find . -name "*.opencover.xml" -exec sh -c 'cp "$0" "coverage/coverage-$(basename $0)"' {} \; - - - name: List coverage files - run: ls '${{ github.workspace }}/coverage/' - - - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - -# - name: coveralls -# uses: coverallsapp/github-action@master -# with: -# github-token: ${{secrets.GITHUB_TOKEN }} -# path-to-lcov: coverage/coverage.info diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml deleted file mode 100644 index da37015..0000000 --- a/.github/workflows/nuget.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: nuget - -on: - push: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - nuget-pack: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 9.0.x - - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --configuration Release - # - name: Test - # run: dotnet test --configuration Release - - name: Pack - run: dotnet pack -p:IncludeSymbols=false -p:SymbolPackageFormat=snupkg --configuration Release - - - name: publish nuget packages - run: | - shopt -s globstar - for file in **/*.nupkg - do - dotnet nuget push "$file" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate - done \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..690c714 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,227 @@ +name: Release + +on: + push: + branches: [ main ] + workflow_dispatch: + +env: + DOTNET_VERSION: '9.0.x' + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + outputs: + version: ${{ steps.version.outputs.version }} + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Extract version from Directory.Build.props + id: version + run: | + VERSION=$(grep -oPm1 "(?<=)[^<]+" Directory.Build.props) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version from Directory.Build.props: $VERSION" + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --configuration Release --no-build --verbosity normal + + - name: Pack NuGet packages + run: dotnet pack --configuration Release --no-build --output ./artifacts + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: nuget-packages + path: ./artifacts/*.nupkg + retention-days: 5 + + publish-nuget: + name: Publish to NuGet + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + outputs: + published: ${{ steps.publish.outputs.published }} + version: ${{ needs.build.outputs.version }} + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + name: nuget-packages + path: ./artifacts + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Publish to NuGet + id: publish + continue-on-error: true + run: | + set +e + OUTPUT="" + PUBLISHED=false + + for package in ./artifacts/*.nupkg; do + echo "Publishing $package..." + RESULT=$(dotnet nuget push "$package" \ + --api-key ${{ secrets.NUGET_API_KEY }} \ + --source https://api.nuget.org/v3/index.json \ + --skip-duplicate 2>&1) + EXIT_CODE=$? + echo "$RESULT" + OUTPUT="$OUTPUT$RESULT" + + if [ $EXIT_CODE -eq 0 ]; then + echo "Successfully published $package" + PUBLISHED=true + elif echo "$RESULT" | grep -q "already exists"; then + echo "Package already exists, skipping..." + else + echo "Failed to publish $package" + exit 1 + fi + done + + if [ "$PUBLISHED" = true ] || echo "$OUTPUT" | grep -q "Your package was pushed"; then + echo "published=true" >> $GITHUB_OUTPUT + echo "At least one package was successfully published" + else + echo "published=false" >> $GITHUB_OUTPUT + echo "No new packages were published (all already exist)" + fi + + create-release: + name: Create GitHub Release and Tag + needs: publish-nuget + runs-on: ubuntu-latest + if: needs.publish-nuget.outputs.published == 'true' + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + name: nuget-packages + path: ./artifacts + + - name: Create and push tag + id: create_tag + run: | + VERSION="${{ needs.publish-nuget.outputs.version }}" + TAG="v$VERSION" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + if git rev-parse "$TAG" >/dev/null 2>&1; then + echo "Tag $TAG already exists" + echo "tag_exists=true" >> $GITHUB_OUTPUT + else + echo "Creating tag $TAG" + git tag -a "$TAG" -m "Release $VERSION" + git push origin "$TAG" + echo "tag_exists=false" >> $GITHUB_OUTPUT + fi + + - name: Get previous tag + id: prev_tag + run: | + CURRENT_TAG="v${{ needs.publish-nuget.outputs.version }}" + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^$CURRENT_TAG$" | tail -n1 || echo "") + if [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ] || [ -z "$PREVIOUS_TAG" ]; then + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v "^$CURRENT_TAG$" | head -n1 || echo "") + fi + echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT + echo "Current tag: $CURRENT_TAG" + echo "Previous tag: $PREVIOUS_TAG" + + - name: Generate release notes + id: release_notes + run: | + VERSION="${{ needs.publish-nuget.outputs.version }}" + CURRENT_TAG="v$VERSION" + PREVIOUS_TAG="${{ steps.prev_tag.outputs.previous_tag }}" + + echo "# Release $VERSION" > release_notes.md + echo "" >> release_notes.md + echo "Released on $(date +'%Y-%m-%d')" >> release_notes.md + echo "" >> release_notes.md + + if [ -n "$PREVIOUS_TAG" ]; then + echo "## ๐Ÿ“‹ Changes since $PREVIOUS_TAG" >> release_notes.md + echo "" >> release_notes.md + + echo "### โœจ Features" >> release_notes.md + git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^feat" --grep="^feature" >> release_notes.md || true + echo "" >> release_notes.md + + echo "### ๐Ÿ› Bug Fixes" >> release_notes.md + git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^fix" --grep="^bugfix" >> release_notes.md || true + echo "" >> release_notes.md + + echo "### ๐Ÿ“š Documentation" >> release_notes.md + git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^docs" --grep="^doc" >> release_notes.md || true + echo "" >> release_notes.md + + echo "### ๐Ÿ”ง Other Changes" >> release_notes.md + git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --invert-grep --grep="^feat" --grep="^feature" --grep="^fix" --grep="^bugfix" --grep="^docs" --grep="^doc" >> release_notes.md || true + echo "" >> release_notes.md + else + echo "## ๐ŸŽ‰ Initial Release" >> release_notes.md + echo "" >> release_notes.md + echo "### Recent Changes" >> release_notes.md + git log --pretty=format:"- %s (%h)" --max-count=20 >> release_notes.md + echo "" >> release_notes.md + fi + + echo "" >> release_notes.md + echo "## ๐Ÿ“ฆ NuGet Packages" >> release_notes.md + echo "" >> release_notes.md + for package in ./artifacts/*.nupkg; do + PACKAGE_NAME=$(basename "$package" .nupkg) + BASE_NAME=$(echo "$PACKAGE_NAME" | sed "s/\.$VERSION//") + echo "- [$BASE_NAME v$VERSION](https://www.nuget.org/packages/$BASE_NAME/$VERSION)" >> release_notes.md + done + + echo "" >> release_notes.md + echo "---" >> release_notes.md + echo "*This release was automatically created by GitHub Actions*" >> release_notes.md + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.publish-nuget.outputs.version }} + name: v${{ needs.publish-nuget.outputs.version }} + body_path: release_notes.md + draft: false + prerelease: false + files: ./artifacts/*.nupkg + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Directory.Build.props b/Directory.Build.props index 5feb71e..ea64fc9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,7 +36,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ManagedCode.Orleans.Identity.Client/ManagedCode.Orleans.Identity.Client.csproj b/ManagedCode.Orleans.Identity.Client/ManagedCode.Orleans.Identity.Client.csproj index d014ea5..51dab41 100644 --- a/ManagedCode.Orleans.Identity.Client/ManagedCode.Orleans.Identity.Client.csproj +++ b/ManagedCode.Orleans.Identity.Client/ManagedCode.Orleans.Identity.Client.csproj @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/ManagedCode.Orleans.Identity.Core/ManagedCode.Orleans.Identity.Core.csproj b/ManagedCode.Orleans.Identity.Core/ManagedCode.Orleans.Identity.Core.csproj index cf443d8..16f391a 100644 --- a/ManagedCode.Orleans.Identity.Core/ManagedCode.Orleans.Identity.Core.csproj +++ b/ManagedCode.Orleans.Identity.Core/ManagedCode.Orleans.Identity.Core.csproj @@ -17,10 +17,10 @@ - - + + - + diff --git a/ManagedCode.Orleans.Identity.Server/ManagedCode.Orleans.Identity.Server.csproj b/ManagedCode.Orleans.Identity.Server/ManagedCode.Orleans.Identity.Server.csproj index f5809c2..50c8b50 100644 --- a/ManagedCode.Orleans.Identity.Server/ManagedCode.Orleans.Identity.Server.csproj +++ b/ManagedCode.Orleans.Identity.Server/ManagedCode.Orleans.Identity.Server.csproj @@ -16,11 +16,11 @@ - - - - - + + + + + diff --git a/ManagedCode.Orleans.Identity.Tests/ManagedCode.Orleans.Identity.Tests.csproj b/ManagedCode.Orleans.Identity.Tests/ManagedCode.Orleans.Identity.Tests.csproj index d9febe4..f636fe5 100644 --- a/ManagedCode.Orleans.Identity.Tests/ManagedCode.Orleans.Identity.Tests.csproj +++ b/ManagedCode.Orleans.Identity.Tests/ManagedCode.Orleans.Identity.Tests.csproj @@ -10,10 +10,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -21,9 +21,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -39,15 +39,15 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + - - + + diff --git a/README.md b/README.md index e592324..6208046 100644 --- a/README.md +++ b/README.md @@ -2,123 +2,123 @@ # Orleans.Identity -A simplified Orleans library for handling authorization context propagation from ASP.NET Core controllers and SignalR hubs to Orleans grains. +Orleans.Identity expands ASP.NET Core authentication and authorization into Orleans grains. It forwards the `ClaimsPrincipal` +created by ASP.NET Identity (JWT, cookies, etc.) to grains, validates `[Authorize]` attributes inside the cluster, and exposes +helpers that make the current user available inside grain code. -## Overview +The repository ships three NuGet packages: -This library provides a simple way to pass user authorization context from your ASP.NET Core application to Orleans grains, allowing you to implement authorization at the grain level using standard ASP.NET Core authorization attributes. +| Package | Purpose | +| --- | --- | +| `ManagedCode.Orleans.Identity.Server` | Registers an Orleans incoming grain call filter that enforces ASP.NET Core authorization attributes in the silo. | +| `ManagedCode.Orleans.Identity.Client` | Adds MVC and SignalR filters that copy the authenticated `ClaimsPrincipal` into Orleans `RequestContext` before grains are invoked. | +| `ManagedCode.Orleans.Identity.Core` | Shared helpers (claim surrogates, extensions, constants). | -## Features +## Key capabilities -- **JWT-based authentication**: Works with standard JWT tokens -- **Controller authorization**: Automatically passes user claims to grains called from controllers -- **SignalR authorization**: Supports authorization in SignalR hubs -- **Grain-level authorization**: Use `[Authorize]` and `[Authorize(Roles = "RoleName")]` attributes on grains -- **Simple grain extension**: Use `this.GetCurrentUser().Claims` to access user claims in grains +- **Authorization parity with ASP.NET Core** โ€“ Grains honor `[Authorize]`, `[AllowAnonymous]`, and role restrictions declared on +grains or grain interfaces. Unauthorized calls throw `UnauthorizedAccessException` before grain logic runs. +- **Automatic claim propagation** โ€“ HTTP controllers and SignalR hubs copy the authenticated user into Orleans `RequestContext` +so that the grain filter can evaluate claims and roles consistently. +- **Grain-side helpers** โ€“ Call `this.GetCurrentUser()` inside a grain to access the callerโ€™s `ClaimsPrincipal` without repeating +boilerplate request-context lookups. +- **SignalR and REST coverage** โ€“ Integration tests verify JWT, cookie, and SignalR scenarios end-to-end with role checks and +anonymous access rules. -## Quick Start +## Getting started -### 1. Orleans Cluster Setup +### 1. Configure the Orleans silo ```csharp -var builder = Host.CreateDefaultBuilder(args) +var host = Host.CreateDefaultBuilder(args) .UseOrleans(siloBuilder => { siloBuilder .UseLocalhostClustering() - .AddOrleansIdentity(); // Add the authorization filter; - }); + .AddOrleansIdentity(); // registers the authorization grain filter + }) + .Build(); + +await host.RunAsync(); ``` -### 2. ASP.NET Core API Setup +The extension registers `GrainAuthorizationIncomingFilter`, which inspects grain metadata and enforces ASP.NET authorization +attributes inside the silo. + +### 2. Configure the ASP.NET Core host ```csharp var builder = WebApplication.CreateBuilder(args); -// Add Orleans client +builder.Services.AddControllers(); +builder.Services.AddSignalR(); + +// Add authentication (JWT, cookies, etc.) +builder.Services.AddAuthentication(/* your schemes */); + +// Forward ClaimsPrincipal values to Orleans +builder.Services.AddOrleansIdentity(); + builder.Services.AddOrleansClient(client => { client.UseLocalhostClustering(); }); -// Add Orleans Identity -builder.Services.AddOrleansIdentity(); - -// Add JWT authentication -builder.Services.AddAuthentication("Bearer") - .AddJwtBearer("Bearer", options => { /* JWT configuration */ }); - var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); -app.UseOrleansIdentity(); // Add the middleware app.MapControllers(); -app.MapHub("/hub"); +app.MapHub("/chat"); + +app.Run(); ``` -### 3. Using in Grains +The MVC and SignalR filters installed by `AddOrleansIdentity` push the authenticated user into `RequestContext` whenever a +controller action or hub method is invoked. + +### 3. Enforce authorization in grains ```csharp [Authorize] -public class MyGrain : Grain, IMyGrain +public interface IUserGrain : IGrainWithGuidKey { - [AllowAnonymous] - public Task GetPublicInfo() - { - return Task.FromResult("Public info"); - } + Task GetProfileAsync(); - [Authorize] - public Task GetUserInfo() + [Authorize(Roles = "Admin")] + Task GetAdminPanelAsync(); +} + +public class UserGrain : Grain, IUserGrain +{ + public Task GetProfileAsync() { var user = this.GetCurrentUser(); - var username = user.FindFirst(ClaimTypes.Name)?.Value; - return Task.FromResult($"Hello, {username}!"); + return Task.FromResult($"Hello, {user.Identity?.Name ?? "anonymous"}!"); } - [Authorize(Roles = "Admin")] - public Task GetAdminInfo() + public Task GetAdminPanelAsync() { - return Task.FromResult("Admin only info"); + return Task.FromResult("Admin only data"); } } ``` -## Testing - -The library includes comprehensive integration tests in the `ManagedCode.Orleans.Identity.Tests` project that demonstrate: +When the grain call arrives, the filter validates the callerโ€™s authentication state and roles before executing grain logic, and +the grain extension retrieves the callerโ€™s claims for business logic. -- JWT token generation and validation -- Controller โ†’ Grain authorization flow -- SignalR โ†’ Grain authorization flow -- Role-based access control -- Grain authorization with user claims +## Testing -### Running Tests +Run the integration suite to exercise the ASP.NET + Orleans pipeline: ```bash dotnet test ``` -### Test Structure - -The tests use the existing integration test infrastructure with: -- **TestApp**: ASP.NET Core application with controllers and SignalR hubs -- **Cluster**: Orleans test cluster with grains -- **Integration Tests**: Comprehensive tests covering all scenarios - -## Architecture - -The library works by: - -1. **Middleware**: Extracts user claims from JWT tokens and stores them in Orleans `RequestContext` -2. **SignalR Filter**: Handles authorization in SignalR hubs and stores claims in `RequestContext` -3. **Grain Filter**: Intercepts grain calls and validates authorization based on `[Authorize]` attributes -4. **Grain Extension**: Provides `this.GetCurrentUser()` method to access claims in grains +The tests spin up an Orleans test cluster and an ASP.NET Core host to validate JWT, cookie, and SignalR flows, including role +checks and anonymous endpoints. ## License MIT License - From 1f158c5880280296dced196eae00f243c7a25f68 Mon Sep 17 00:00:00 2001 From: ksemenenko Date: Sun, 2 Nov 2025 20:33:46 +0100 Subject: [PATCH 2/2] bump, version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index ea64fc9..ad33ba8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -24,8 +24,8 @@ https://github.com/managedcode/Orleans.Identity https://github.com/managedcode/Orleans.Identity Managed Code - Orleans Identity - 9.0.0 - 9.0.0 + 9.0.1 + 9.0.1