aspnet / Security

[Archived] Middleware for security and authorization of web apps. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
1.27k stars 599 forks source link

microsoftaccount retrieving access_token accross requests on ASP.NET CORE 2.0 #1706

Closed Mimetis closed 6 years ago

Mimetis commented 6 years ago

Hi all; I can't figured out how to retrieve the access_token once authenticated from within the MicrosoftAccount provider.
Well, to be honest, I should be able to retrieve it if it's stored in a data store using the _signinManager instance.

Storing the token in my datastore

await _signInManager.UpdateExternalAuthenticationTokensAsync(info);

Following this issue https://github.com/aspnet/Identity/issues/1452

Making a SQL call on every requests does not seems correct to me.

Getting the value from a cookie / claim / datastore ?

I tried to set the tokens in a cookie, or a claims, but actually, I didn't find anyway to make it work...

Here is my extract code:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddMicrosoftAccount(microsoftOptions =>
{
    var azureAdOptions = Configuration.GetSection("AD").Get<AdConfig>();

    microsoftOptions.ClientId = azureAdOptions.ClientId;
    microsoftOptions.ClientSecret = azureAdOptions.Secret;

    microsoftOptions.SaveTokens = true;

    microsoftOptions.Events.OnCreatingTicket = (option) =>
    {
        option.Properties.IsPersistent = true;
        option.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(5);
        List<AuthenticationToken> tokens = option.Properties.GetTokens() as List<AuthenticationToken>;
        tokens.Add(new AuthenticationToken() { Name = "TicketCreated", Value = DateTime.UtcNow.ToString() });
        option.Properties.StoreTokens(tokens);

        return Task.CompletedTask;
    };

})
.AddCookie()

The microsoftOptions.SaveTokens = true; allows me to retrieve the tokens on call back and then saved tokens using the SigninManager.
It's working, but not want I want, actually...

During the OnCreatingTicket event, I tried several things. My last attempt was to try to set an expired time on the cookie generation:

option.Properties.IsPersistent = true;
option.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(5);

Finally, after sucessfully log in, I try to retrieve my tokens, with several methods, but none of them are working, yet :)

if (User.Identity.IsAuthenticated)
{
    // 1) Using siginmanager
    var info = await _signInManager.GetExternalLoginInfoAsync();

    // 2 Using http context
    string accessToken = await HttpContext.GetTokenAsync("access_token");

    // 3 Using http context with scheme
    string accessToken2 = await HttpContext.GetTokenAsync(MicrosoftAccountDefaults.AuthenticationScheme, "access_token");

    // 4 Using http context with scheme
    //string accessToken3 = await HttpContext.GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, "access_token");

    // 5 Using the Identity.Application scheme 
    string at = await HttpContext.GetTokenAsync("Identity.Application", "access_token");

    // 6 Using the Identity.Application scheme 
    // Seems to be what signinManager actually did.
    string at2 = await HttpContext.GetTokenAsync("Identity.External", "access_token");

}

I tried to change some properties like the default schema, trying services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) or services.AddAuthentication(MicrosoftAccountDefaults.AuthenticationScheme) but nothing works actually.

Do you have any idea ?

Mimetis commented 6 years ago

Ok, got it.

You have to save the tokens, since the Identity.External cookie seems to be deleted after the authentication (do not hesitate to correct me if I'm wrong) So the deal is to add the tokens after Post back authentication

if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                var props = new AuthenticationProperties();
                props.IsPersistent = true;
                props.ExpiresUtc = DateTime.UtcNow.AddDays(5);
                props.StoreTokens(info.AuthenticationTokens);
                await _signInManager.SignInAsync(user, props, info.LoginProvider);

                // Add this to add token to datastore
                // Update the token
                await _signInManager.UpdateExternalAuthenticationTokensAsync(info);

                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
                return LocalRedirect(Url.GetLocalUrl(returnUrl));
            }
        }

This is not an easy path to get something that seems to me a basic scenario in my opinion ? What do you think ?

Other bonus question : How do you handle the token expiration ?

Tratcher commented 6 years ago

Duplicate of https://github.com/aspnet/Security/issues/1668, https://github.com/aspnet/Security/issues/1685