diff --git a/Handler.Auth/ITokenHandler.cs b/Handler.Auth/ITokenHandler.cs index 6653096..38e251f 100644 --- a/Handler.Auth/ITokenHandler.cs +++ b/Handler.Auth/ITokenHandler.cs @@ -13,7 +13,6 @@ public interface ITokenHandler /// Current token cache is in memory and will need to be updated to an external store before production worthy /// /// All needed parameters to obtain an access token - /// Task StoreAccessToken(T allTokenNeededData); /// diff --git a/ModernAuth_API/Controllers/ValuesController.cs b/ModernAuth_API/Controllers/ValuesController.cs index 569d029..f918bd7 100644 --- a/ModernAuth_API/Controllers/ValuesController.cs +++ b/ModernAuth_API/Controllers/ValuesController.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Net.Http.Headers; -using System.Security.Claims; using System.Threading.Tasks; using Handler.Auth; using Microsoft.AspNetCore.Authentication; @@ -12,7 +9,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Graph; -using Newtonsoft.Json; namespace ModernAuth_API.Controllers { @@ -38,24 +34,52 @@ public ValuesController(IConfiguration configuration, [HttpGet("{username}")] public async Task Get(string username) { - var dictionary = _configuration.GetSection("AzureAd").GetChildren() + var tokenData = await GetTokenData(); + + var graphAccessToken = await _tokenHandler.GetAccessTokenOnBehalfOf(tokenData); + + var user = await GetUserInfo(tokenData["userName"], graphAccessToken); + + return new ObjectResult(user); + } + + + /// + /// Returns data needed to acquire a new token on behalf of the signed in user that targets the Microsoft Graph API + /// + /// + private async Task> GetTokenData() + { + var tokenData = _configuration.GetSection("AzureAd").GetChildren() .Select(item => new KeyValuePair(item.Key, item.Value)) .ToDictionary(x => x.Key, x => x.Value); //adding accessToken used to call this api to dictionary of token data - dictionary["accessToken"] = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); + tokenData["accessToken"] = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); + + tokenData["resource"] = _configuration["resource"]; + tokenData["userName"] = _httpContextAccessor.HttpContext.User.Identity.Name; + tokenData["RedirectURI"] = _configuration["RedirectURI"]; - dictionary["resource"] = _configuration["resource"]; - dictionary["userName"] = _httpContextAccessor.HttpContext.User.Identity.Name; - dictionary["RedirectURI"] = _configuration["RedirectURI"]; - var accessToken = await _tokenHandler.GetAccessTokenOnBehalfOf(dictionary); + return tokenData; + } - //this initiates a graph service client using ADAL. Using MSAL is documented here https://docs.microsoft.com/en-us/graph/sdks/create-client?context=graph%2Fapi%2F1.0&view=graph-rest-1.0&tabs=CS - var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => { + /// + /// Returns user's basic profile from the Microsoft Graph API + /// + /// user to query basic profile for + /// access token for calling the Microsoft Graph APi + /// + private static async Task GetUserInfo(string userName, string graphAccessToken) + { + //this initiates a graph service client using a DelegateAuthenticationProvider https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/docs/overview.md#delegateauthenticationprovider + //Using an MSAL based auth provider is documented here https://docs.microsoft.com/en-us/graph/sdks/create-client?context=graph%2Fapi%2F1.0&view=graph-rest-1.0&tabs=CS + var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => + { requestMessage .Headers - .Authorization = new AuthenticationHeaderValue("bearer", accessToken); + .Authorization = new AuthenticationHeaderValue("bearer", graphAccessToken); return Task.FromResult(0); })); @@ -64,16 +88,15 @@ public async Task Get(string username) //query options allow passing in OData queries to the api request List options = new List { - new QueryOption("$filter", $"userPrincipalName eq '{username}'") + new QueryOption("$filter", $"userPrincipalName eq '{ userName }'") }; - //user will be of type array which is why FirstOrDefault is used to obtain the actual user model + //user will be an array which is why FirstOrDefault is used to obtain the actual user model var user = await graphServiceClient.Users .Request(options) .GetAsync(); - return new ObjectResult(user.FirstOrDefault()); + return user.FirstOrDefault(); } - } } diff --git a/ModernAuth_UI/Extensions/AzureAdAuthenticationBuilderExtensions.cs b/ModernAuth_UI/Extensions/AzureAdAuthenticationBuilderExtensions.cs index 144f3f6..61aa1e2 100644 --- a/ModernAuth_UI/Extensions/AzureAdAuthenticationBuilderExtensions.cs +++ b/ModernAuth_UI/Extensions/AzureAdAuthenticationBuilderExtensions.cs @@ -73,18 +73,12 @@ public void Configure(OpenIdConnectOptions options) //Used to handle when an authCode has been received. Not added by configuration wizard private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) { - - var dictionary = _configuration.GetSection("AzureAd").GetChildren() - .Select(item => new KeyValuePair(item.Key, item.Value)) - .ToDictionary(x => x.Key, x => x.Value); - - dictionary["Code"] = context.TokenEndpointRequest.Code; - dictionary["userName"] = context.Principal.Identity.Name; + var tokenData = GetTokenData(context); //used to exchange the authCode for an access and refresh token and then store those tokens in the token cache. //Current token cache is in memory and will need to be updated to an external store before production worthy //Not added by configuration wizard - await _tokenHandler.StoreAccessToken(dictionary); + await _tokenHandler.StoreAccessToken(tokenData); //used to obtain the idToken passed in on the signin request var idToken = context.ProtocolMessage.IdToken; @@ -102,6 +96,21 @@ private Task OnAuthenticationFailed(AuthenticationFailedContext context) return Task.FromResult(0); } + /// + /// Returns data needed to acquire a new token on behalf of the signed in user that targets the Microsoft Graph API + /// + /// + private IDictionary GetTokenData(AuthorizationCodeReceivedContext context) + { + var dictionary = _configuration.GetSection("AzureAd").GetChildren() + .Select(item => new KeyValuePair(item.Key, item.Value)) + .ToDictionary(x => x.Key, x => x.Value); + + dictionary["Code"] = context.TokenEndpointRequest.Code; + dictionary["userName"] = context.Principal.Identity.Name; + return dictionary; + } + } } }