Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.23.0" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.30" />
<PackageReference Include="Microsoft.Graph" Version="4.11.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.39.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.9" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.15.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.15.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.14.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Protocols;

namespace ServerlessMicroservices.Shared.Services
{
Expand All @@ -30,6 +31,7 @@ public class TokenValidationService : ITokenValidationService
private DiscoveryCache _discoveryCache;
private string _audience;
private string _scope;
private string _authority;

/// <summary>
/// Configured by the function app settings. If false, validation is skipped.
Expand All @@ -50,6 +52,7 @@ public TokenValidationService(ISettingService settingService, ILoggerService log

_audience = settingService.GetApiApplicationId();
_scope = settingService.GetApiScopeName();
_authority = settingService.GetAuthorityUrl();
AuthEnabled = settingService.EnableAuth();
}

Expand Down Expand Up @@ -88,6 +91,9 @@ async Task<ClaimsPrincipal> ValidateJwt(string token)
var handler = new JwtSecurityTokenHandler();
handler.InboundClaimTypeMap.Clear();

// Debugging purposes only, set this to false for production
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

try
{
var principal = handler.ValidateToken(token, validationParams, out _);
Expand All @@ -114,25 +120,33 @@ async Task<TokenValidationParameters> GetValidationParameters()
return null;
}

var keys = disco.KeySet.Keys
.Where(x => x.Use == SigningKeyUseType)
.Select(x =>
{
return new RsaSecurityKey(new System.Security.Cryptography.RSAParameters
{
Exponent = Base64Url.Decode(x.E),
Modulus = Base64Url.Decode(x.N)
})
{
KeyId = x.Kid
};
});
ConfigurationManager<OpenIdConnectConfiguration> configManager =
new ConfigurationManager<OpenIdConnectConfiguration>(
$"{_authority}/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());

OpenIdConnectConfiguration config = null;
config = await configManager.GetConfigurationAsync();

//var keys = disco.KeySet.Keys
// .Where(x => x.Use == SigningKeyUseType)
// .Select(x =>
// {
// return new RsaSecurityKey(new System.Security.Cryptography.RSAParameters
// {
// Exponent = Base64Url.Decode(x.E),
// Modulus = Base64Url.Decode(x.N)
// })
// {
// KeyId = x.Kid
// };
// });

return new TokenValidationParameters
{
ValidIssuer = disco.Issuer,
ValidAudience = _audience,
IssuerSigningKeys = keys,
IssuerSigningKeys = config.SigningKeys,
NameClaimType = NameClaimType,
RoleClaimType = RoleClaimType
};
Expand Down
35 changes: 19 additions & 16 deletions dotnet/ServerlessMicroservices.Shared/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Identity.Client;
using Newtonsoft.Json;

namespace ServerlessMicroservices.Shared.Services
{
public class UserService : IUserService
{
const string GraphBaseUrl = "https://graph.windows.net/";
const string GraphVersionQueryString = "?" + GraphVersion;
const string GraphVersion = "api-version=1.6";
const string GraphBaseUrl = "https://graph.microsoft.com/v1.0";
const string GraphVersionQueryString = "?";

private string[] _scopes = new[] { "https://graph.microsoft.com/.default" };
private IConfidentialClientApplication _app;
private readonly string _authority = "https://login.microsoftonline.com/";


private readonly AuthenticationContext _authContext;
private readonly ClientCredential _clientCreds;
private readonly string _graphUrl;

public UserService(ISettingService settingService)
: this(settingService.GetGraphTenantId(), settingService.GetGraphClientId(), settingService.GetGraphClientSecret())
Expand All @@ -28,22 +29,24 @@ public UserService(string tenantId, string clientId, string clientSecret)
{
if (string.IsNullOrEmpty(tenantId)) throw new ArgumentNullException(nameof(tenantId), "GraphTenantId environment variable must be set before instantiating UserService.");

_graphUrl = GraphBaseUrl + tenantId;
_authority = _authority + tenantId;

var authority = "https://login.microsoftonline.com/" + tenantId;
_authContext = new AuthenticationContext(authority);
_clientCreds = new ClientCredential(clientId, clientSecret);
_app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(_authority))
.Build();
}

async Task<string> GetAccessToken()
{
var authResult = await _authContext.AcquireTokenAsync(GraphBaseUrl, _clientCreds);
return authResult.AccessToken;
var result = await _app.AcquireTokenForClient(_scopes)
.ExecuteAsync();
return result.AccessToken;
}

public async Task<(User, string error)> CreateUser(CreateUser newUser)
{
var url = _graphUrl + "/users" + GraphVersionQueryString;
var url = GraphBaseUrl + "/users";

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessToken());
Expand All @@ -70,7 +73,7 @@ async Task<string> GetAccessToken()

public async Task<(IEnumerable<User>, string error)> GetUsers()
{
var url = _graphUrl + "/users" + GraphVersionQueryString;
var url = GraphBaseUrl + "/users";

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessToken());
Expand Down Expand Up @@ -98,7 +101,7 @@ async Task<string> GetAccessToken()
{
if (String.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));

var url = _graphUrl + "/users/" + userId + GraphVersionQueryString;
var url = GraphBaseUrl + "/users/" + userId;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessToken());
Expand Down
Loading