无法使用 B2C 和 AddMicrosoftIdentityWebApp 在 Blazor 应用程序中获取刷新令牌

发布于 2025-01-15 01:14:33 字数 7028 浏览 4 评论 0原文

我有一个 Blazor 应用程序,当前仅使用 id 令牌进行身份验证,有效期为 24 小时。我希望能够使用刷新令牌,但无法从 C# 代码访问它们。

但是,我知道该应用程序已正确配置,因为我可以使用邮递员获取刷新令牌: 邮差结果

这家初创公司的样子是这样的:

services.AddAuthentication(options =>
                {
                    options.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                })
                .AddMicrosoftIdentityWebApp(options =>
                    {
                        options.SignedOutRedirectUri = "/";
                        options.SignInScheme = OpenIdConnectDefaults.AuthenticationScheme;
                        options.GetClaimsFromUserInfoEndpoint = true;
                        options.TokenValidationParameters.SaveSigninToken = true;
                        options.TokenValidationParameters.ValidateIssuer = true;
                        options.TokenValidationParameters.IssuerValidator = ValidateSpecificIssuers;
                        options.TokenValidationParameters.ValidateLifetime = true;
                        options.TokenValidationParameters.LifetimeValidator = ValidateLifetime;

                        options.Events.OnRedirectToIdentityProvider = ctx =>
                        {
                            if (ctx.Properties.Items.ContainsKey("login_hint"))
                                ctx.ProtocolMessage.LoginHint = ctx.Properties.Items["login_hint"];

                            if (ctx.Properties.Items.ContainsKey("domain_hint"))
                                ctx.ProtocolMessage.DomainHint = ctx.Properties.Items["domain_hint"];

                            var request = ctx.HttpContext.Request;
                            ctx.ProtocolMessage.RedirectUri = $"{request.Scheme}://{request.Host}/signin-oidc";

                            return Task.FromResult(0);
                        };

                        options.Events.OnAuthenticationFailed = async ctx =>
                        {
                            if (ctx.Result?.Succeeded ?? false)
                                return;

                            if (ctx.Request.Path == "/auth/logout")
                            {
                                ctx.HandleResponse();
                                return;
                            }

                            var body = await ctx.Request.Body.ReadToStringAsync();
                            var form = await ctx.Request.ReadFormAsync();

                            ctx.Principal = null;
                            ctx.HttpContext.User = null;
                            Console.WriteLine($"Authentication failed:\r\n{ctx.Exception?.Message}\r\n{ctx.Exception?.StackTrace}");
                            ctx.Response.Redirect("/auth/logout");
                            ctx.HandleResponse();
                            return;
                        };

                        options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
                        options.TenantId = Configuration["AzureAdB2C:TenantId"];
                        options.Instance = Configuration["AzureAdB2C:Instance"];
                        options.ClientId = Configuration["AzureAdB2C:ClientId"];
                        options.Domain = Configuration["AzureAdB2C:Domain"];
                        options.SignedOutCallbackPath = Configuration["AzureAdB2C:SignedOutCallbackPath"];
                        options.SignUpSignInPolicyId = Configuration["AzureAdB2C:SignUpSignInPolicyId"];
                        options.ResetPasswordPolicyId = Configuration["AzureAdB2C:ResetPasswordPolicyId"];
                        options.EditProfilePolicyId = Configuration["AzureAdB2C:EditProfilePolicyId"];
                        options.ClientSecret = Configuration["AzureAdB2C:ClientSecret"];
                        options.SaveTokens = true;

                        options.Scope.Add("offline_access"); // in order to get the refresh token
                    }
                )
                .EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["BlazorServer:Scope"] })
                .AddDownstreamWebApi("BlazorServer", configuration.GetSection("BlazorServer"))
                .AddSessionTokenCaches();

            services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, (OpenIdConnectOptions options) =>
            {
                options.Scope.Add("offline_access");

                options.Events.OnTokenValidated += ctx =>
                {
                    // ctx.TokenEndpointResponse.RefreshToken is null!

                    var token = ctx.SecurityToken as JwtSecurityToken;
                    ctx.Principal.AddIdentity(new ClaimsIdentity(new Claim[]
                    {
                        new Claim("access_token", token.RawData)
                    }));

                    ctx.Principal.AddIdentity(new ClaimsIdentity(
                        token.Claims,
                        "jwt",
                        ctx.Options.TokenValidationParameters.NameClaimType,
                        ctx.Options.TokenValidationParameters.RoleClaimType
                    ));

                    return Task.CompletedTask;
                };


                options.Events.OnRemoteFailure += async (RemoteFailureContext context) =>
                {
                    var response = await context.Response.Body.ReadToStringAsync();

                    var responseQuery = response.Contains("consent_required") ? "&response=consent" : "";
                    context.Response.Redirect($"/Error?error={context.Failure}{responseQuery}");
                    
                    context.HandleResponse();
                };
            });

我的最终目标是拥有另一个令牌声明,即刷新令牌(我已经拥有访问令牌)。

我在另一篇文章中读到,此令牌可以由 Microsoft MSAL 自动处理: 使用 Azure AD V2.0 (MSAL) 和 Asp .Net Core 2.0 获取刷新令牌

但是,情况似乎并非如此我的结束,因为用户在令牌过期时会自动注销。这是我在 B2C 中的令牌配置,

<Metadata>

            <Item Key="client_id">{service:te}</Item>

            <Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item>

            <Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item>

            <Item Key="token_lifetime_secs">500</Item>

            <Item Key="id_token_lifetime_secs">300</Item>

            <Item Key="refresh_token_lifetime_secs">86400</Item>

            <Item Key="rolling_refresh_token_lifetime_secs">86400</Item>

          </Metadata>

我还发现我可以调用 GetTokenAsync(),但为了调用它,我需要用户缓存。这是唯一的方法吗?

非常感谢您的光临:)

I have a Blazor application that currently uses id tokens only for authentication with an expiration of 24h. I would like to be able to use refresh tokens, but can't access them from the C# code.

However, I know that the app is correctly configured since I can get a refresh token using postman:
Postman Result

Here is what the startup looks like:

services.AddAuthentication(options =>
                {
                    options.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                })
                .AddMicrosoftIdentityWebApp(options =>
                    {
                        options.SignedOutRedirectUri = "/";
                        options.SignInScheme = OpenIdConnectDefaults.AuthenticationScheme;
                        options.GetClaimsFromUserInfoEndpoint = true;
                        options.TokenValidationParameters.SaveSigninToken = true;
                        options.TokenValidationParameters.ValidateIssuer = true;
                        options.TokenValidationParameters.IssuerValidator = ValidateSpecificIssuers;
                        options.TokenValidationParameters.ValidateLifetime = true;
                        options.TokenValidationParameters.LifetimeValidator = ValidateLifetime;

                        options.Events.OnRedirectToIdentityProvider = ctx =>
                        {
                            if (ctx.Properties.Items.ContainsKey("login_hint"))
                                ctx.ProtocolMessage.LoginHint = ctx.Properties.Items["login_hint"];

                            if (ctx.Properties.Items.ContainsKey("domain_hint"))
                                ctx.ProtocolMessage.DomainHint = ctx.Properties.Items["domain_hint"];

                            var request = ctx.HttpContext.Request;
                            ctx.ProtocolMessage.RedirectUri = 
quot;{request.Scheme}://{request.Host}/signin-oidc";

                            return Task.FromResult(0);
                        };

                        options.Events.OnAuthenticationFailed = async ctx =>
                        {
                            if (ctx.Result?.Succeeded ?? false)
                                return;

                            if (ctx.Request.Path == "/auth/logout")
                            {
                                ctx.HandleResponse();
                                return;
                            }

                            var body = await ctx.Request.Body.ReadToStringAsync();
                            var form = await ctx.Request.ReadFormAsync();

                            ctx.Principal = null;
                            ctx.HttpContext.User = null;
                            Console.WriteLine(
quot;Authentication failed:\r\n{ctx.Exception?.Message}\r\n{ctx.Exception?.StackTrace}");
                            ctx.Response.Redirect("/auth/logout");
                            ctx.HandleResponse();
                            return;
                        };

                        options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
                        options.TenantId = Configuration["AzureAdB2C:TenantId"];
                        options.Instance = Configuration["AzureAdB2C:Instance"];
                        options.ClientId = Configuration["AzureAdB2C:ClientId"];
                        options.Domain = Configuration["AzureAdB2C:Domain"];
                        options.SignedOutCallbackPath = Configuration["AzureAdB2C:SignedOutCallbackPath"];
                        options.SignUpSignInPolicyId = Configuration["AzureAdB2C:SignUpSignInPolicyId"];
                        options.ResetPasswordPolicyId = Configuration["AzureAdB2C:ResetPasswordPolicyId"];
                        options.EditProfilePolicyId = Configuration["AzureAdB2C:EditProfilePolicyId"];
                        options.ClientSecret = Configuration["AzureAdB2C:ClientSecret"];
                        options.SaveTokens = true;

                        options.Scope.Add("offline_access"); // in order to get the refresh token
                    }
                )
                .EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["BlazorServer:Scope"] })
                .AddDownstreamWebApi("BlazorServer", configuration.GetSection("BlazorServer"))
                .AddSessionTokenCaches();

            services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, (OpenIdConnectOptions options) =>
            {
                options.Scope.Add("offline_access");

                options.Events.OnTokenValidated += ctx =>
                {
                    // ctx.TokenEndpointResponse.RefreshToken is null!

                    var token = ctx.SecurityToken as JwtSecurityToken;
                    ctx.Principal.AddIdentity(new ClaimsIdentity(new Claim[]
                    {
                        new Claim("access_token", token.RawData)
                    }));

                    ctx.Principal.AddIdentity(new ClaimsIdentity(
                        token.Claims,
                        "jwt",
                        ctx.Options.TokenValidationParameters.NameClaimType,
                        ctx.Options.TokenValidationParameters.RoleClaimType
                    ));

                    return Task.CompletedTask;
                };


                options.Events.OnRemoteFailure += async (RemoteFailureContext context) =>
                {
                    var response = await context.Response.Body.ReadToStringAsync();

                    var responseQuery = response.Contains("consent_required") ? "&response=consent" : "";
                    context.Response.Redirect(
quot;/Error?error={context.Failure}{responseQuery}");
                    
                    context.HandleResponse();
                };
            });

My ultimate goal would be to have another token claim which woud be the refresh token (I already have access token).

I've read on another post that this token could be automatically handled by Microsoft MSAL: Get refresh token with Azure AD V2.0 (MSAL) and Asp .Net Core 2.0

However, it does not appear to be the case on my end, since the user gets automatically logged out upon token expiration. Here is the token configuration I have in B2C

<Metadata>

            <Item Key="client_id">{service:te}</Item>

            <Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item>

            <Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item>

            <Item Key="token_lifetime_secs">500</Item>

            <Item Key="id_token_lifetime_secs">300</Item>

            <Item Key="refresh_token_lifetime_secs">86400</Item>

            <Item Key="rolling_refresh_token_lifetime_secs">86400</Item>

          </Metadata>

I've also seend that I could call GetTokenAsync(), but in order to call that I need a cache of users. Is that the only way of doing it?

Thanks a lot for stopping by :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

才能让你更想念 2025-01-22 01:14:33

我们可以使用以下解决方法来获取 .net 应用程序的刷新令牌。

以下是您可以遵循的解决方法:

- 尝试在 appsettings.json 文件中添加以下内容

"OpenIdConnect": {
"ResponseType": "code id_token token",
"Scope": [ "offline_access", "https://xxx.onmicrosoft.com/xxxx-xxc-xxx-xxxx-xxxxxx/Management" ],
"SaveTokens": "true" // Save access token and refresh token
`}

startup.cs 中添加以下

services.Configure<OpenIdConnectOptions>(AzureADB2CDefaults.OpenIdScheme, options =>
{
Configuration.Bind("OpenIdConnect", options);
}

内容 有关完整信息,请参阅以下链接:-

We can use the below workaround to get refresh token for our .net application .

Below are workaround you can follow:

-Try to add the following in your appsettings.json file

"OpenIdConnect": {
"ResponseType": "code id_token token",
"Scope": [ "offline_access", "https://xxx.onmicrosoft.com/xxxx-xxc-xxx-xxxx-xxxxxx/Management" ],
"SaveTokens": "true" // Save access token and refresh token
`}

Following in your startup.cs

services.Configure<OpenIdConnectOptions>(AzureADB2CDefaults.OpenIdScheme, options =>
{
Configuration.Bind("OpenIdConnect", options);
}

For complete information please refer the below links:-

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文