我很难理解IdentityServer3,AzureAD和私有数据库是如何协同工作的。最大的问题是如何处理重定向URI。
我的情况是我有一个独立的IdentityServer3。它的工作是根据AzureAD或私有数据库对用户进行身份验证。在ID3服务器上的Startup.cs文件中,我有以下OpenID Connect代码:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", s3App =>
{
s3App.UseIdentityServer(new IdentityServerOptions
{
SiteName = "3S",
SigningCertificate = Certificate.Load(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(InMemoryUsers.Get())
.UseInMemoryClients(InMemoryClients.Get())
.UseInMemoryScopes(InMemoryScopes.Get()),
AuthenticationOptions = new AuthenticationOptions
{
EnablePostSignOutAutoRedirect = true,
EnableSignOutPrompt = false,
IdentityProviders = ConfigureAdditionalIdentityProviders
}
});
});
}
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "AzureAd",
Caption = "Login",
ClientId = "4613ed32-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // GUID of registered application on Azure
Authority = "https://login.microsoftonline.com/our-tenant-id/",
PostLogoutRedirectUri = "https://localhost:44348/identity",
RedirectUri = "https://localhost:44348/identity",
Scope = "openid email profile",
ResponseType = "id_token",
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = signInAsType,
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = Constants.ExternalAuthenticationType,
ValidateIssuer = false
}
});
}
我不明白为什么ID3服务器需要RedirectUri或PostLogoutRedirectUri ...不应该从请求身份验证的应用程序“传递”?毕竟,我们想要回到应用程序,而不是ID3服务器。当然,我不认为这是导致我的问题的原因,但是理解为什么这些问题会很好。
我会说,我已经“接近”了这项工作。
当我的应用程序要求身份验证请求针对AzureAD进行身份验证时,我被重定向到Microsoft帐户登录屏幕以输入我的工作帐户的用户名/密码。我提交我的凭据,然后重定向回ID3服务器或我的应用程序,具体取决于上述代码中使用的RedirectUri。
为了论证,我们假设我将我的应用程序用于RedirectUri。我将被发送回应用程序,但不会发送到最初提示身份验证质询的页面,如果我单击需要身份验证的页面,我会被发送回AzureAD服务器再次登录,这次只是AzureAD认出我已登录。
不幸的是,从AzureAD重定向后,似乎没有确认/设置SecurityTokenValidated通知。
以下是应用程序Startup.cs中的代码:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44348/identity",
ClientId = "3af8e3ba-5a04-4acc-8c51-1d30f8587ced", // Local ClientID registered as part of the IdentityServer3 InMemoryClients
Scope = "openid profile roles",
RedirectUri = "http://localhost:52702/",
PostLogoutRedirectUri = "http://localhost:52702/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);
var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);
var sub = id.FindFirst(Constants.ClaimTypes.Subject);
var roles = id.FindAll(Constants.ClaimTypes.Role);
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role
);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaim(sub);
nid.AddClaims(roles);
nid.AddClaim(new Claim("application_specific", "Some data goes here. Not sure what, though."));
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
return Task.FromResult(0);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Error/message=" + context.Exception.Message);
//Debug.WriteLine("*** AuthenticationFailed");
return Task.FromResult(0);
},
}
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
}
}
您会注意到OpenIdConnectAuthenticationOptions还包含指向应用程序的RedirectUri和PostLogoutRedirectUri,但这些似乎并不重要。
当然,当我使用“cookies”方面登录时,一切都很完美 - 我看到了我对用户的所有要求。并且,花了一些时间与微软通电话,他们提出了一个ID3以外的解决方案,但它不是我们需要的方式。我们将有多个应用程序针对我们的ID3进行身份验证,因此我们需要在内部包含和控制流程。
我真的需要一些帮助才能弄清楚最后一英里问题。我知道我很接近,我只是一直盯着这看,所以我可能正在盯着我的错误而没有看到它。
2016年10月22日编辑
进一步测试并启用Serilog显示RedirectUri
和PostLogoutRedirectUri
的问题导致我将/identity
添加到URI的末尾,这对应于app.Map中设置的值。这解决了我被返回到IdentityServer3的“空白”页面的问题,我现在返回到IdentityServer3登录屏幕。 Azure AD仍然认为我已登录,我只是没有在我的应用程序中正确设置令牌。
答案 0 :(得分:2)
首先,重定向URL需要注册到身份提供者,因此服务器将匹配请求中的RedirectURL,以确保响应按预期重定向,而不是重定向到类似网络钓鱼站点(安全性考虑)。
如图所示,要使用Azure AD作为IdentityServer3的外部身份提供程序,我们需要在Azure AD上注册应用程序。但是,由于该应用程序用于与Identity Server通信,因此Azure门户上的重定向URL注册应重定向到IdentityServer3,而不是应用程序的URL。
例如,我的身份服务器3的URL是https://localhost:44333,然后我使用下面的代码添加其他身份提供者。此URL是Azure门户上的重定向URL:
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "aad",
Caption = "Azure AD",
SignInAsAuthenticationType = signInAsType,
Authority = "https://login.microsoftonline.com/04e14a2c-0e9b-42f8-8b22-3c4a2f1d8800",
ClientId = "eca61fd9-f491-4f03-a622-90837bbc1711",
RedirectUri = "https://localhost:44333/core/aadcb",
});
}
我的应用程序的URL是http://localhost:1409/,它在IdentyServer3上注册,下面代码是Web应用程序使用OWIN OpenId Connect将IdentyServer3添加为身份数据提供者:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "cookies",
Authority = "https://localhost:44333",
ClientId = "mvc",
RedirectUri = "http://localhost:1409/",
ResponseType = "id_token",
Scope = "openid profile email"
});