diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..654b3d1
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# Basic dependabot.yml file with
+
+version: 2
+updates:
+ - package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 2
+ allow:
+ - dependency-name: "EAVFramework"
\ No newline at end of file
diff --git a/.github/workflows/.releaserc b/.github/workflows/.releaserc
new file mode 100644
index 0000000..43302f1
--- /dev/null
+++ b/.github/workflows/.releaserc
@@ -0,0 +1,22 @@
+branches:
+ - name: main
+ - name: dev
+ prerelease: dev
+
+plugins:
+ - - "@semantic-release/commit-analyzer"
+ - preset: conventionalcommits
+
+ - - "@semantic-release/release-notes-generator"
+ - preset: conventionalcommits
+
+ - - "@semantic-release/github"
+ - assets:
+ - path: ../../artifacts/*.nupkg
+ label: Parser DLL
+
+ - - "@semantic-release/exec"
+ - publishCmd: "dotnet nuget push ..\\..\\artifacts\\*.nupkg --source https://nuget.pkg.github.com/eavfw/index.json --api-key ${process.env.GITHUB_TOKEN}"
+
+ - - "@semantic-release/exec"
+ - publishCmd: "dotnet nuget push ..\\..\\artifacts\\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${process.env.CI_NUGET_API_KEY}"
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..1c618ce
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,33 @@
+
+on:
+ pull_request:
+ types: [ assigned, opened, synchronize, reopened ]
+ push:
+ branches:
+ - 'feature/**'
+
+jobs:
+ build:
+ runs-on: windows-latest
+ name: Building
+ steps:
+ - name: Checkout code base
+ uses: actions/checkout@v2
+
+ - uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Cleaning
+ run: dotnet clean
+
+ - name: Build solution
+ run: dotnet build -c Release
+
+ #- name: Archive build to artifacts
+ # uses: actions/upload-artifact@v2
+ # with:
+ # name: build
+ # path: |
+ # build/*
+ # retention-days: 5
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..9d5c4ea
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,61 @@
+name: Release
+
+on:
+ push:
+ branches:
+ - dev
+ - main
+
+jobs:
+ release:
+ name: Releasing
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+
+ - uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '8.0.x'
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: '20'
+
+ - name: Add plugin for conventional commits
+ run: npm install conventional-changelog-conventionalcommits@7.0.2
+ working-directory: ./.github/workflows
+
+ - name: Add plugin for executing bash commands
+ run: npm install @semantic-release/exec -D
+ working-directory: ./.github/workflows
+
+ - name: Dry Run Semantic to get next Version nummber
+ working-directory: ./.github/workflows
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GIT_AUTHOR_NAME: pksorensen
+ GIT_AUTHOR_EMAIL: poul@kjeldager.com
+ run: |
+ echo "RELEASE_VERSION=$((npx semantic-release --dry-run).Where({ $_ -like '*Release note*' }) | Out-String | Select-String '[0-9]+\.[0-9]+\.[0-9]+([-][a-zA-z]+[.][0-9]*)?' | % { $_.Matches } | % { $_.Value })" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+ - name: Print release verison
+ run: echo ${env:RELEASE_VERSION}
+
+ - name: Cleaning
+ run: dotnet clean
+
+ - name: Restore NuGet packages
+ run: dotnet restore
+
+ - name: Package Parser
+ run: dotnet pack -c Release -p:PackageVersion=${env:RELEASE_VERSION} -o ./artifacts
+ if: ${{ env.RELEASE_VERSION }}
+
+ - name: Release to GitHub and NuGet
+ working-directory: .\\.github\\workflows
+ env:
+ CI_NUGET_API_KEY: ${{ secrets.CI_NUGET_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GIT_AUTHOR_NAME: pksorensen
+ GIT_AUTHOR_EMAIL: poul@kjeldager.com
+ run: npx semantic-release
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..fd4b3e2
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,10 @@
+
+
+
+ 10.0
+ 4.2.8
+ true
+ $(MSBuildThisFileDirectory)/external/EAVFramework
+ $(MSBuildThisFileDirectory)/external
+
+
\ No newline at end of file
diff --git a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/EAVFW.Extensions.EasyAuth.MicrosoftEntraId.csproj b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/EAVFW.Extensions.EasyAuth.MicrosoftEntraId.csproj
index 80d65e7..491b88b 100644
--- a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/EAVFW.Extensions.EasyAuth.MicrosoftEntraId.csproj
+++ b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/EAVFW.Extensions.EasyAuth.MicrosoftEntraId.csproj
@@ -18,9 +18,13 @@
-
+
+
+
+
+
+
-
diff --git a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraEasyAuthProvider.cs b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraEasyAuthProvider.cs
index 8f74ff6..6177054 100644
--- a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraEasyAuthProvider.cs
+++ b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraEasyAuthProvider.cs
@@ -1,12 +1,26 @@
-using EAVFramework.Authentication;
+using EAVFramework.Authentication;
+using EAVFramework.Extensions;
+using IdentityModel;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Net.Sockets;
using System.Security.Claims;
+using System.Text;
using System.Threading.Tasks;
+using System.Web;
using static IdentityModel.OidcConstants;
+using static System.Net.WebRequestMethods;
namespace EAVFW.Extensions.EasyAuth.MicrosoftEntraId
{
@@ -14,6 +28,7 @@ namespace EAVFW.Extensions.EasyAuth.MicrosoftEntraId
public class MicrosoftEntraEasyAuthProvider : IEasyAuthProvider
{
private readonly IOptions _options;
+ private readonly IHttpClientFactory _clientFactory;
public string AuthenticationName => "MicrosoftEntraId";
@@ -23,38 +38,56 @@ public class MicrosoftEntraEasyAuthProvider : IEasyAuthProvider
public MicrosoftEntraEasyAuthProvider() { }
- public MicrosoftEntraEasyAuthProvider(IOptions options)
+ public MicrosoftEntraEasyAuthProvider(IOptions options, IHttpClientFactory clientFactory)
{
_options = options ?? throw new System.ArgumentNullException(nameof(options));
+ _clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory));
}
public async Task OnAuthenticate(HttpContext httpcontext, string handleId, string redirectUrl)
{
var email = httpcontext.Request.Query["email"].FirstOrDefault();
var redirectUri = httpcontext.Request.Query["redirectUri"].FirstOrDefault();
-
- // var url =$"{oauthEndpoint}"
+
var ru = new RequestUrl(_options.Value.AuthorizationUrl);
-
- //var authUri = ru.CreateAuthorizeUrl(_options.Value.ClientI
- // responseType: ResponseTypes.Code,
- // redirectUri: _options.Value.RedirectUri,
- // responseMode: ResponseModes.FormPost ,
-
- // // extra: new Parameters { { "consentId", provider.ExternalId } },
- // // codeChallenge: challenge,
- // // nonce: nonce,
- // // responseMode: ResponseModes.FormPost,
- // //scope: "payments:inbound payments:outbound accounts offline_access",
-
- // state: handleId);
-
-
+ var authUri = ru.CreateAuthorizeUrl(
+ clientId: _options.Value.ClientId,
+ redirectUri: _options.Value.RedirectUrl,
+ responseType: ResponseTypes.Code,
+ responseMode: ResponseModes.FormPost,
+ scope: _options.Value.Scope,
+ state: handleId + "&" + redirectUri);
+ httpcontext.Response.Redirect(authUri);
}
- public Task<(ClaimsPrincipal, string, string)> OnCallback(HttpContext httpcontext)
+ public async Task<(ClaimsPrincipal, string, string)> OnCallback(HttpContext httpcontext)
{
- throw new System.NotImplementedException();
+ var m = new IdentityModel.Client.AuthorizeResponse(await new StreamReader(httpcontext.Request.Body).ReadToEndAsync());
+ var state = m.State.Split(new char[] { '&' }, 2);
+ var handleId = state[0];
+ var redirectUri = state[1];
+ var http = _clientFactory.CreateClient();
+ var response = await http.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
+ {
+ Address = _options.Value.TokenEndpoint,
+ ClientId = _options.Value.ClientId,
+ ClientSecret = _options.Value.ClientSecret,
+ Code = m.Code,
+ RedirectUri = _options.Value.RedirectUrl,
+ });
+
+ var handler = new JwtSecurityTokenHandler();
+ var jwtSecurityToken = handler.ReadJwtToken(response.IdentityToken);
+ var jti = jwtSecurityToken.Claims.First(claim => claim.Type == "email").Value;
+
+ ClaimsPrincipal identity = await _options.Value.ValidateUserAsync(httpcontext, handleId, response);
+
+ if (identity == null)
+ {
+ httpcontext.Response.Redirect("error=access_denied&error_subcode=user_not_found");
+ //return;
+ }
+ return await Task.FromResult((new ClaimsPrincipal(identity), redirectUri, handleId));
}
public RequestDelegate OnSignedOut()
diff --git a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthExtensions.cs b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthExtensions.cs
index 008387d..466371e 100644
--- a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthExtensions.cs
+++ b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthExtensions.cs
@@ -1,16 +1,23 @@
-using EAVFramework.Configuration;
+using EAVFramework.Configuration;
+using IdentityModel.Client;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Security.Claims;
+using System.Threading.Tasks;
namespace EAVFW.Extensions.EasyAuth.MicrosoftEntraId
{
public static class MicrosoftEntraIdEasyAuthExtensions
{
- public static AuthenticatedEAVFrameworkBuilder AddMicrosoftEntraIdEasyAuth(this AuthenticatedEAVFrameworkBuilder builder)
+ public static AuthenticatedEAVFrameworkBuilder AddMicrosoftEntraIdEasyAuth(this AuthenticatedEAVFrameworkBuilder builder, Func> validateUserAsync)
{
-
builder.AddAuthenticationProvider((options, config) =>
- {
+ {
+ config.GetSection("EAVEasyAuth:MicrosoftEntraId").Bind(options);
+ options.ValidateUserAsync = validateUserAsync;
});
return builder;
diff --git a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthOptions.cs b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthOptions.cs
index 0f514f5..2b7100a 100644
--- a/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthOptions.cs
+++ b/src/EAVFW.Extensions.EasyAuth.MicrosoftEntraId/MicrosoftEntraIdEasyAuthOptions.cs
@@ -1,9 +1,23 @@
-namespace EAVFW.Extensions.EasyAuth.MicrosoftEntraId
+using IdentityModel.Client;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Threading.Tasks;
+
+namespace EAVFW.Extensions.EasyAuth.MicrosoftEntraId
{
public class MicrosoftEntraIdEasyAuthOptions
{
public string AuthorizationUrl { get; set; }
public string ClientId { get; set; }
- public string RedirectUri { get; set; }
+ public string ClientSecret { get; set; }
+ public string TenantId { get; set; }
+ public string GroupId { get; set; }
+ public string Scope { get; set; }
+ public string TokenEndpoint { get; set; }
+ public string RedirectUrl { get; set; }
+
+ public Func> ValidateUserAsync { get; set; }
}
}
\ No newline at end of file