Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate Microsoft GraphClient with signed-in user's AccessToken?

My Startup.Auth.cs looks like this:

public partial class Startup
{
    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
    private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];

    public static readonly string Authority = aadInstance + tenantId;

    // This is the resource ID of the AAD Graph API.  We'll need this to request a token to call the Graph API.
    string graphResourceId = "https://graph.microsoft.com";

    public void ConfigureAuth(IAppBuilder app)
    {
        ApplicationDbContext db = new ApplicationDbContext();

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = Authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
                   AuthorizationCodeReceived = (context) => 
                   {
                       var code = context.Code;
                       ClientCredential credential = new ClientCredential(clientId, appKey);
                       string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                       TokenCache userTokenCache = new ADALTokenCache(signedInUserID);

                       AuthenticationContext authContext = new AuthenticationContext(Authority, userTokenCache);
                       AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                       code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);

                       string token = result.AccessToken;

                       return Task.FromResult(0);
                   }
                }
            });
    }
}

The second-to-last line of code above (the string variable "token") correctly contains the currently signed-in user's AccessToken. Later, my web application project attempts to instantiate a Microsoft GraphClient object, like so:

GraphServiceClient graphClient = new GraphServiceClient(new AuthenticationController());

When the above code is called, then a new AuthenticationController is created, like so:

public class AuthenticationController : IAuthenticationProvider
{
    private string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
    private string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];

    public async Task AuthenticateRequestAsync(HttpRequestMessage request)
    {

        string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
        string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

        // get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
        ClientCredential creds = new ClientCredential(clientId, appKey);

        TokenCache tokenCache = new ADALTokenCache(signedInUserID);
        // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
        AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, tokenCache);
        AuthenticationResult authResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com/", creds);

        request.Headers.Add("Authorization", "Bearer " + authResult.AccessToken);
    }
}

My issue is that the AuthenticationController code above retrieves the AccessToken associated with the web application, not the currently signed-in user. I need to instantiate a GraphClient with the AccessToken of the currently signed-in user instead of the web application. Specifically, I would like the variable authResult.AccessToken in last line of code to contain the AccessToken of the currently signed-in user. Is there any way that I can securely save the currently signed-in user's token cache in Startup.Auth.cs and then later retrieve it the AuthenticationController class?

like image 398
aBlaze Avatar asked Oct 23 '25 04:10

aBlaze


1 Answers

If you already have a token, you call tell GraphServiceClient to simply use that token rather than attempting to fetch one all over again. This is done using the DelegateAuthenticationProvider class:

var graphserviceClient = new GraphServiceClient(
    new DelegateAuthenticationProvider(
        (requestMessage) =>
        {
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);

            return Task.FromResult(0);
        }));

Where you store that token depends on the type of app and your architecture. For web apps you can store it in the Session or a Cookie, for native apps in memory. Tokens are short-lived so any temporary store is generally acceptable.

like image 186
Marc LaFleur Avatar answered Oct 25 '25 18:10

Marc LaFleur



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!