Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Rob 4 posts 76 karma points
    Mar 01, 2022 @ 11:43
    Rob
    0

    Persist and refresh Access Token after external Microsoft B2C login

    Hi,

    https://our.umbraco.com/documentation/reference/security/external-login-providers/ https://our.umbraco.com/documentation/reference/security/auto-linking/

    I've adapted the documentation here to work with Azure AD B2C. I can login successfully, and the auto linking works as intended. However, my website requires the use of a Azure AD protected API. This means that I need to use the Azure AD access token (that I get from the callback from Microsoft AD) as a Bearer for a subsequent REST API call.

    How do I keep hold of the access token? And, if the token expires after ~15 mins, how would I refresh that token? Thanks

  • Paul Johnson 18 posts 109 karma points
    Mar 03, 2022 @ 11:47
    Paul Johnson
    0

    Hey Rob,

    Couple of questions

    • Is this for backoffice users or for members?
    • Are you intending to call the protected API on behalf of each "user" or is it the application itself which needs to communicate with the protected API.
  • Paul Johnson 18 posts 109 karma points
    Mar 03, 2022 @ 11:55
    Paul Johnson
    0

    The user/member tokens can be retieved on behalf of a user/member with IExternalLoginWithKeyService.GetExternalLoginTokens (although this may not be working correctly for members just yet).

    They are stored in the database in dbo.umbracoExternalLoginToken, if you check that table do you have access token and refresh token records?

  • Paul Johnson 18 posts 109 karma points
    Mar 03, 2022 @ 16:52
    Paul Johnson
    0

    I have been playing around with this today to ensure it's working for members as well as users, so I have code snippets if it's useful (sorry it's for Auth0 instead of Azure AD but setup should be moderatey similar, possibly a little more verbose, I opted for quick and easy).

    public class DemoComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.AddBackOfficeExternalLogins(logins =>
            {
                logins.AddBackOfficeLogin(
                    cfg =>
                    {
                        cfg.AddAuth0WebAppAuthentication("Umbraco.Auth0",
                                opt =>
                                {
                                    opt.CallbackPath = "/umbraco-auth0-signin";
                                    opt.Domain = builder.Config["Auth0:Domain"];
                                    opt.ClientId = builder.Config["Auth0:ClientId"];
                                    opt.ClientSecret = builder.Config["Auth0:ClientSecret"];
                                    opt.Scope = "openid profile email offline_access";
                                })
                            .WithAccessToken(opt =>
                            {
                                opt.Audience = builder.Config["Auth0:DemoProtectedApi"];
                            });
                    });
            });
        }
    }
    
    public class ExposeMyTokensController : Controller
    {
        private readonly IUserService _userService;
        private readonly IExternalLoginWithKeyService _externalLogins;
    
        public ExposeMyTokensController(IUserService userService, IExternalLoginWithKeyService externalLogins)
        {
            _userService = userService;
            _externalLogins = externalLogins;
        }
    
        [HttpGet("demo")]
        public IActionResult BadIdea()
        {
            var me = _userService.GetUserById(-1);
            var myTokens = _externalLogins.GetExternalLoginTokens(me.Key);
            return Ok(myTokens);
        }
    }
    

    If I link my backoffice user with an external account and hit the demo endpoint above I can see all my tokens rendered

    enter image description here

  • Rob 4 posts 76 karma points
    Mar 04, 2022 @ 16:21
    Rob
    0

    Hi Paul.

    Thanks for your help. I'm trying the equivalent for members, but getting nothing here:

    var me = _memberService.GetById(int.Parse(User.Identity.GetUserId()));
    var myTokens = _externalLogins.GetExternalLoginTokens(me.Key);
    

    'myTokens' is null.

    I'm not sure if this is Members vs Users thing, or it's something specific to Azure AD.

    I noticed in the Umbraco external login examples for Azure AD, there is a 'options.SaveTokens = true'. This throws an NotImplemented exception if I set this to true, however. Might be a red herring, but it sounds related.. the Umbraco source makes it looks it's been omitted by design, but I can't think why..

  • Paul Johnson 18 posts 109 karma points
    Mar 11, 2022 @ 15:49
    Paul Johnson
    0

    If you're interested the rc for 9.4 is out and this should hopefully work there if you're up for being a tester.

    https://www.nuget.org/packages/Umbraco.Cms/9.4.0-rc

  • Paul Johnson 18 posts 109 karma points
    Mar 04, 2022 @ 20:58
    Paul Johnson
    0

    Yep this won't work for members just yet, fixed for 9.4 in https://github.com/umbraco/Umbraco-CMS/pull/12093

  • Rob 4 posts 76 karma points
    Mar 25, 2022 @ 09:49
    Rob
    1

    Hi Paul,

    Thanks for adding that fix! Adding 'saveTokens = true' no longer throws an exception. I think I'm still doing something wrong, however.

    _externalLogins.GetExternalLoginTokens(me.Key);
    

    is now an empty list rather than null. The 'umbracoExternalLoginToken' table is empty in the db.

    Here's my config:

    public static class MicrosoftMemberAuthenticationExtensions
    {
        public static IUmbracoBuilder AddMicrosoftB2CMemberAuthentication(this IUmbracoBuilder builder)
        {
            builder.Services.ConfigureOptions<MicrosoftB2CMemberExternalLoginProviderOptions>();
    
            builder.AddMemberExternalLogins(logins =>
            {
                logins.AddMemberLogin(
                    memberAuthenticationBuilder =>
                    {
                        memberAuthenticationBuilder.AddOpenIdConnect(
                            // The scheme must be set with this method to work for the back office
                            memberAuthenticationBuilder.SchemeForMembers(MicrosoftB2CMemberExternalLoginProviderOptions.SchemeName),
                            options =>
                            {
                                options.Events = new OpenIdConnectEvents
                                {
                                    OnTokenValidated = context =>
                                    {
                                        //Mapping 'emails' to the required 'email'
                                        var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
                                        claimsIdentity.AddClaim(new Claim("email", context.Principal.FindFirst("emails").Value));
                                        return Task.FromResult(0);
                                    }
                                };
    
                                // use cookies
                                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
                                // pass configured options along
                                options.Authority = "xxxxxxxxxxxxxx";
                                options.ClientId = "xxxxxxxxxxxxxx";
                                options.ClientSecret = "xxxxxxxxxxxxxx";
                                options.CallbackPath = "/signin-oidc";
    
                                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                                {
                                    ValidateIssuer = false,
                                    SaveSigninToken = true
                                };
    
                                // Use the authorization code flow
                                options.ResponseType = OpenIdConnectResponseType.Code;
                                options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
    
                                options.RequireHttpsMetadata = true;
                                options.GetClaimsFromUserInfoEndpoint = true;
    
                                options.Scope.Add("xxxxxxxxxxxxxx");
                                options.UsePkce = true;
    
                                options.SaveTokens = true;  //now allowed in 9.4
                            });
                    });
            });
            return builder;
        }
    }
    

    Logging in and autolinking works perfectly still.. It's just the retrieval of the external login token afterwards that's still alluding me. Any ideas?

    Thanks

  • Rob 4 posts 76 karma points
    Mar 29, 2022 @ 10:41
    Rob
    1

    Ah - ignore me. It's working (sort of!) if I removed all the members pre-9.4 then re-registered.

    The issue I'm having now is that the 'access token' only gets set on the INITIAL auto-link of the member. Any access token used to login subsequently is not getting saved into the 'umbracoExternalLoginTokens' table. I'm not sure if there is a setting I'd need to change somewhere, or if this a bug. Paul - I don't have a 'refresh token' at all, like you do in your screenshot - not sure if that's something to do with it.

    enter image description here

    Is there a mechanism for manually refreshing access tokens built into Umbraco's external login providers?

  • Paul Johnson 18 posts 109 karma points
    Mar 29, 2022 @ 16:29
    Paul Johnson
    0

    You won't get a refresh token unless the scope offline_access is requested.

    Once you have that you should be able to exchange the refresh token for a new access token.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Jul 28, 2022 @ 23:00
    Jeroen Breuer
    0

    Hi Rob,

    I'm running into the same issue. It seems the data in the umbracoExternalLoginToken table is outdated. If you remove the lines from the table they will be added again after the next login. But somehow they are not updated once they exists.

    This also returns the old tokens:

    var currentMember = await _memberManager.GetCurrentMemberAsync();
    var currentMemberIdentityUser = (MemberIdentityUser)currentMember;
    var loginTokens = currentMemberIdentityUser.LoginTokens;
    

    I've been trying to debug this for a few hours, but it's unclear why the tokens are outdated.

    Did you ever created a bug for this?

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Jul 29, 2022 @ 08:07
    Jeroen Breuer
    0

    Hi Rob,

    I created an issue for this: https://github.com/umbraco/Umbraco-CMS/issues/12749

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Aug 17, 2022 @ 15:36
    Jeroen Breuer
    0

    The issue is fixed in this PR: https://github.com/umbraco/Umbraco-CMS/pull/12856

    It will be part of Umbraco 10.2!

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Sep 01, 2022 @ 09:42
    Jeroen Breuer
    0

    Umbraco 10.2 RC is out and the issue is fixed there: https://github.com/umbraco/Umbraco-CMS/issues/12749

    I've also released a package which uses the umbracoExternalLoginTokens table now that it's fixed: https://www.jeroenbreuer.nl/blog/released-umbraco-openid-connect-example-package/

    Jeroen

  • Gurumurthy 52 posts 125 karma points
    Jun 13, 2023 @ 06:21
    Gurumurthy
    0

    Hello All,

    I am integrating with azure b2c authentication using the open id connect, by using the github, shared (https://github.com/jbreuer/Umbraco-OpenIdConnect-Example). this is for member authentication I am able to authenticate to my azure b2c and receiving all the token, but one of the scope parameter is missing from my access token. This is was tested in umbraco v11.3, and we I do have v8 implementation for the same b2c to get access token, in dot net framework i am getting the scope parameter, but in core the scope parameter is not listing. Also, this si an external api scope that is configured.

    https://xxxxdevb2c.onmicrosoft.com/web-api/api-scope this is the scope value which used in v8 dot net framework, but not able to add this type of url scope in dot net core.. as per below:

    options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("offline_access");

    Any suggestions, on how to add the url type scope in dot net core openid connect.

    Thanks,

  • Biagio Paruolo 1593 posts 1824 karma points c-trib
    Feb 24, 2024 @ 07:15
    Biagio Paruolo
    0

    How to use with the access token when the login page is an external application?

Please Sign in or register to post replies

Write your reply to:

Draft