问题
I've been put in charge of figuring out a way to allow users to authenticate into our forms based site. Our corporate IT has set up a development adfs server, and the relying party trust has been set up to one of our dev environments.
I've been reading and looking for tutorials for about two weeks, so I'm by no means an expert, and I can't seem to understand how the STS (our ADFS server) is supposed to figure out which user is requesting authentication.
I've been following Wiktor Zychla's blog because it actually includes code examples and messes less with the web.config as opposed to most other tutorials. (http://www.wiktorzychla.com/2014/11/simplest-saml11-federated-authentication.html)
Here's the pipeline so far as I can see it.
- User comes to the log in page.
- Page_Load automatically redirects user to ADFS
- User gets authenticated and redirected back to the log in page
- In the Page_Load I then consume some token and use that to authorize the user.
- Redirect the user to a page that requires authentication.
Code and Web.config changes I've made to the site:
protected void Page_Load(object sender, System.EventArgs e)
{
var sam = FederatedAuthentication.SessionAuthenticationModule;
var fam = new WSFederationAuthenticationModule();
fam.FederationConfiguration = FederatedAuthentication.FederationConfiguration;
var request = new HttpContextWrapper(this.Context).Request;
if (UseADFS)
{
// is this the response from the STS
if (!fam.IsSignInResponse(request))
{
// no
// the STS
fam.Issuer = WsFederationIssuerName;
// the return address
fam.Realm = WsRealm;
fam.Reply = WsReply;
var req = fam.CreateSignInRequest(string.Empty, null, false);
// go to STS
Response.Redirect(req.WriteQueryString());
}
// is this the response from the STS
else
{
// yes, get the SAML token
var securityToken = fam.GetSecurityToken(request);
var config = new SecurityTokenHandlerConfiguration
{
CertificateValidator = X509CertificateValidator.None,
IssuerNameRegistry = new CustomIssuerNameRegistry()
};
config.AudienceRestriction.AudienceMode = AudienceUriMode.Never;
var tokenHandler = new SamlSecurityTokenHandler
{
CertificateValidator = X509CertificateValidator.None,
Configuration = config
};
// validate the token and get the ClaimsIdentity out of it
var identity = tokenHandler.ValidateToken(securityToken);
var principal = new ClaimsPrincipal(identity);
var token = sam.CreateSessionSecurityToken(principal, string.Empty,
DateTime.Now.ToUniversalTime(), DateTime.Now.AddMinutes(20).ToUniversalTime(), false);
sam.WriteSessionTokenToCookie(token);
if (identity[0].IsAuthenticated)
{
//string email = principal.Claims.Where(x => x.Type == ClaimTypes.Email).Select(x => x.Value).SingleOrDefault();
string name = principal.Claims.Where(x => x.Type == ClaimTypes.Name).Select(x => x.Value).SingleOrDefault();
CustomClaimsObject claim = new CustomClaimsObject(name, principal);
doSigninWork(true, claim);
}
}
}
else if (!this.IsPostBack && !fam.IsSignInResponse(request))
{
Session.Abandon();
}
}
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</configSections>
<httpModules>
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler"/>
</httpModules>
<modules>
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler"/>
</modules>
<system.identityModel>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration>
<cookieHandler requireSsl="false" />
</federationConfiguration>
</system.identityModel.services>
<authentication mode="Forms">
<forms loginUrl="Public/invalidlogin.aspx?err=sessiontimeout" protection="All" requireSSL="false" slidingExpiration="true"/>
</authentication>
How is the STS supposed to figure out who is requesting authentication? I'm not posting any relevant user info to it. Am I supposed to add something to my request? Or is the context somehow supposed to have relevant data? Or does the ADFS box need to have a log in where the user enters their credentials?
Right now when I navigate to the user to (by way of the button click) https://xxx-xxx.xxxxx.xxxxxx.com/adfs/ls/ I get this error.
Encountered error during federation passive request.
Additional Data
Protocol Name:
wsfed
Relying Party:
Exception details:
Microsoft.IdentityServer.Web.CookieManagers.InvalidContextException: MSIS7001: The passive protocol context was not found or not valid. If the context was stored in cookies, the cookies that were presented by the client were not valid. Ensure that the client browser is configured to accept cookies from this website and retry this request.
at Microsoft.IdentityServer.Web.Protocols.GenericProtocolRequest.ParseEncodedRequestParts(String[] encodedRequestParts)
at Microsoft.IdentityServer.Web.Protocols.GenericProtocolRequest..ctor(String encodedGenericRequest)
at Microsoft.IdentityServer.Web.Protocols.WSFederation.WSFederationProtocolHandler.GetOriginalRequestFromResponse(ProtocolContext context)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.ProcessProtocolRequest(ProtocolContext protocolContext, PassiveProtocolHandler protocolHandler)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)
Can someone kick me in the right direction? I'm sure I'm missing something obvious to anyone with experience.
Update:
Login with adfs working. But now I'm struggling a bit with the logout. My logout code is below. Essentially whats happening is the authentication on my claims identity is still true even after I've attempted to "logout" with the code below. The only claim I'm sending from ADFS to my relying party is en E-mail claim. Not sure if that makes a difference.
WSFederationAuthenticationModule authModule = FederatedAuthentication.WSFederationAuthenticationModule;
string signoutUrl = (WSFederationAuthenticationModule.GetFederationPassiveSignOutUrl(WsFederationIssuerName, eURL.BuildURL(), null));
WSFederationAuthenticationModule.FederatedSignOut(new Uri(signoutUrl), null);
Update 2:
So I've realized the sign out is working correctly. However it seems ADFS is redirecting to my sign in page before it redirects to my log out page. This is causing the Sign in code to be run again which fires another claim to ADFS. Is there a way to stop this? Or a way to know if I'm coming from the adfs logout?
Update 3:
Solved the issue by wrapping an if statement around the if(UseADFS) clause to check to see where I'm coming from.
if(request.UrlReferrer.Authority.Contains(authority))
I just check to see if the place I'm coming from is my server (your adfs domain) vs (your website domain). This together with a parameter placed in my redirect url was enough to decipher whether the user was logging out or logging in.
来源:https://stackoverflow.com/questions/41988205/how-does-wif-wsfederationauthentication-know-which-user-i-am