向Azure Active Directory身份验证传递参数

7
我有一个ASP.Net MVC应用程序,使用Owin,并且还使用Azure Active Directory身份验证。
我想在用户被重定向到Azure AD身份验证页面时传递参数。因此,当用户登录或注册时,我想将ProjectId(int)作为参数传递。
在用户登录/注册并被重定向到我的应用程序后,我希望接收到我传递的ProjectId作为参数。
如何实现这一点?
编辑:添加代码
// The ACR claim is used to indicate which policy was executed
public const string AcrClaimType = "http://schemas.microsoft.com/claims/authnclassreference";
public const string PolicyKey = "b2cpolicy";
private const string OidcMetadataSuffix = "/.well-known/openid-configuration";

public void ConfigureAuth(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    OpenIdConnectAuthenticationOptions options = new OpenIdConnectAuthenticationOptions
    {
        // These are standard OpenID Connect parameters, with values pulled from web.config
        ClientId = ClientId,
        RedirectUri = RedirectUri,
        PostLogoutRedirectUri = RedirectUri,
        UseTokenLifetime = false,
        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            AuthenticationFailed = AuthenticationFailed,
            RedirectToIdentityProvider = OnRedirectToIdentityProvider,
            SecurityTokenValidated = OnSecurityTokenValidated
        },
        Scope = "openid",
        ResponseType = "id_token",

        // The PolicyConfigurationManager takes care of getting the correct Azure AD authentication
        // endpoints from the OpenID Connect metadata endpoint.  It is included in the PolicyAuthHelpers folder.
        ConfigurationManager = new PolicyConfigurationManager(
            string.Format(CultureInfo.InvariantCulture, AadInstance, Tenant, "/v2.0", OidcMetadataSuffix),
            new[] { SignUpPolicyId, SignInPolicyId, ProfilePolicyId }),

        // This piece is optional - it is used for displaying the user's name in the navigation bar.
        TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name"
        }
    };

    app.UseOpenIdConnectAuthentication(options);
}

private Task OnRedirectToIdentityProvider(
        RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
    if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
    {
        var currentPolicy =
            notification.OwinContext.Authentication.AuthenticationResponseRevoke.AuthenticationTypes
                .FirstOrDefault(x => x.StartsWith("b2c"));
        notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.Split('?')[0];
        notification.ProtocolMessage.Parameters.Add("p", currentPolicy);
    }
    else
    {
        **// The value right now for the state is sort of "hijacked" and assigned by Microsoft**
        //notification.ProtocolMessage.Parameters["state"] = "OpenIdConnect.AuthenticationProperties=sRt-teBcxsd239viWo...... ";

        var currentPolicy = notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties
            .Dictionary[PolicyKey];
        notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.Split('?')[0];
        notification.ProtocolMessage.Parameters.Add("p", currentPolicy);
    }

    return Task.FromResult(0);
}

private async Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
    await MyClass.CreatePrincipal(notification.AuthenticationTicket.Identity);
}

private Task AuthenticationFailed(
            AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
    notification.HandleResponse();
    notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
    return Task.FromResult(0);
}
3个回答

10
类似于Gaurav所建议的,但是需要添加一些特殊的考虑因素。基本上,状态由Owin中间件使用,因此,尽管您可以注入自己的东西,但需要确保在Owin中间件尝试使用之前将其恢复,否则您将会得到认证错误。
这实际上就是我回答一个非常相似问题时所说的: 使用Microsoft.Owin.Security.OpenIdConnect和AzureAD v 2.0端点的自定义参数 在Startup.Auth.cs中,当您设置OpenIdConnectAuthenticationOptions时,您应该添加以下内容:
app.UseOpenIdConnectAuthentication(
  new OpenIdConnectAuthenticationOptions
  {
    //...
    Notifications = new OpenIdConnectAuthenticationNotifications
    {
      RedirectToIdentityProvider = OnRedirectToIdentityProvider,
      MessageReceived = OnMessageReceived
    },
  });

使用RedirectToIdentityProvider方法注入参数,类似于以下内容:
private static Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
  var stateQueryString = notification.ProtocolMessage.State.Split('=');
  var protectedState = stateQueryString[1];
  var state = notification.Options.StateDataFormat.Unprotect(protectedState);
  state.Dictionary.Add("mycustomparameter", "myvalue");
  notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
  return Task.FromResult(0);
}

然后使用MessageReceived将其提取,就像这样:

private static Task OnMessageReceived(MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
  string mycustomparameter;
  var protectedState = notification.ProtocolMessage.State.Split('=')[1];
  var state = notification.Options.StateDataFormat.Unprotect(protectedState);
  state.Dictionary.TryGetValue("mycustomparameter", out mycustomparameter);
  return Task.FromResult(0);
}

显然,您需要改进/加强此部分,但这应该能帮助您入门。


1
这个回答让我从深渊中解脱出来了。非常感谢!‍♂️ - WalternativE

4

只需在 RedirectToIdentityProvider 中添加 context.ProtocolMessage.SetParameter(<ParameterName>, <value>);

Notifications = new OpenIdConnectAuthenticationNotifications()
            {
                RedirectToIdentityProvider = context =>
                    {
                        context.ProtocolMessage.SetParameter("prompt", "login");
                        return Task.FromResult(0);
                    },
                
            }
        };

2
您可以将 ProjectId 参数作为 State 参数的值传递。请参见下面的示例代码:
            Notifications = new OpenIdConnectAuthenticationNotifications()
            {
                RedirectToIdentityProvider = context =>
                    {
                        redirectUri = string.Format("{0}/", System.Web.HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority));
                        postLogoutRedirectUri = redirectUri + "sign-out";
                        context.ProtocolMessage.RedirectUri = redirectUri;
                        context.ProtocolMessage.PostLogoutRedirectUri = postLogoutRedirectUri;
                        context.ProtocolMessage.State = "Your Project Id";
                        return Task.FromResult(0);
                    },
                AuthorizationCodeReceived = context =>
                    {
                        var projectId = context.ProtocolMessage.State;//Retrieve the state in AuthorizationCodeReceived event.
                        return Task.FromResult(0);
                    }
            }
        };

更新

本质上,State接受一个字符串参数。在我们的项目中,我们需要在状态中提供许多值。我们所做的是创建一个管道分隔的字符串,并将其作为状态传递。当我们收到状态返回时,我们只需将其转换为数组并使用相应的元素即可。类似于:

var state = "param1|param2|...|paramx";

另外一个你可以做的事情是创建一个状态对象(一个带有一些属性的简单类),将其序列化为JSON,将其转换为base64字符串并在适当地进行URL编码后将该编码字符串作为状态传递。当你收到状态时,可以执行反向过程,获取状态对象并使用其属性值。


Gaurav,非常感谢您的帮助。我无法设置context.ProtocolMessage.State,因为我已经将值分配给了“OpenIdConnect.AuthenticationProperties=sRt-TokenValue”。我甚至尝试设置两个值,但当我更改OpenIdConnect.AuthenticationProperties时,我无法进行身份验证。有什么想法可以传递和接收参数吗? - Bartho Bernsmann
你能否更新你的问题并包含代码?那肯定会有所帮助。 - Gaurav Mantri
谢谢。我更新了我的答案。看看这种方法是否有意义。 - Gaurav Mantri
我尝试了类似的东西,但如果我改变状态,身份验证就无法工作。我也尝试了你的建议,但它没有起作用 :( - Bartho Bernsmann
如果你有其他想法,请告诉我。非常感谢你的帮助! - Bartho Bernsmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接