WIF: ID1014: The signature is not valid. The data may have been tampered with - wif

We've built a Relying Party application based on the Windows Identity Foundation. We followed the advice in Vittorio's book and created a custom set of cookie transforms to use RSA to encrypt/sign the token.
private void OnServiceConfigurationCreated( object sender, ServiceConfigurationCreatedEventArgs e )
{
List<CookieTransform> sessionTransforms = new List<CookieTransform>( new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform( e.ServiceConfiguration.ServiceCertificate ),
new RsaSignatureCookieTransform( e.ServiceConfiguration.ServiceCertificate )
} );
SessionSecurityTokenHandler sessionHandler =
new SessionSecurityTokenHandler( sessionTransforms.AsReadOnly() );
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace( sessionHandler );
}
We configured a in the web.config.
<microsoft.identityModel>
<service>
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint" findValue="C7FD338059CCB374798923A915BC91B718814A8E" storeLocation="LocalMachine" storeName="TrustedPeople" />
</serviceCertificate>
</service>
</microsoft.identityModel>
I know the code in the OnServiceConfigurationCreated is executing because if I put a garbage thumbprint value into the config file the OnServiceConfigurationCreated throws an exception.
Unfortunately we are frequently getting the following exception showing up in our logs.
System.Security.Cryptography.CryptographicException: ID1014: The signature is not valid. The data may have been tampered with.
at Microsoft.IdentityModel.Web.RsaSignatureCookieTransform.Decode(Byte[] encoded)
at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound)
at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver)
at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver)
at Microsoft.IdentityModel.Web.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie)
at Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken)
at Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
We believe this exception is causing other problems in the system but can't figure out why it's occurring. We have three web servers and we've triple-checked that they are all configured to use the same certificate thumbprint and that the certificate is installed in the same place on all three servers.
We are also using a custom SessionAuthenticationModule to handle sliding session expiration. I thought that maybe when that code (below) was reissuing the cookie it might be using a different encryption/signing approach but I'm pretty sure I've tested it and that doesn't seem to be the case. I'm including it only in the interest of full disclosure.
void CustomSessionAuthenticationModule_SessionSecurityTokenReceived( object sender, SessionSecurityTokenReceivedEventArgs e )
{
DateTime now = DateTime.UtcNow;
DateTime validFrom = e.SessionToken.ValidFrom;
DateTime validTo = e.SessionToken.ValidTo;
double tokenLifetime = (validTo - validFrom).TotalMinutes;
SessionAuthenticationModule sam = sender as SessionAuthenticationModule;
if( now < validTo && now > validFrom.AddMinutes( tokenLifetime / 2 ) )
{
e.SessionToken = sam.CreateSessionSecurityToken(
e.SessionToken.ClaimsPrincipal, e.SessionToken.Context,
now, now.AddMinutes( tokenLifetime ), e.SessionToken.IsPersistent );
e.ReissueCookie = true;
}
}
From what we can tell we've done everything the docs/blogs/etc have said but we're still getting this exception. Any tips/pointers/educated guesses would be helpful at this point.

You might want to check the total size of cookie data your application sets. If you include lots of claims, the cookies grow accordingly unless you use session mode. E.g. Safari has a 4K limit on total cookie data size. If you break this limit you'd start losing cookies, which could mean you'd lose a cookie with part of the signature.
As a side note, if you can move to WIF 4.5 you have the option of using the MachineKeySessionSecurityTokenHandler instead of doing the certficate based cookie encryption.

Related

Enabling SSL in ASP.NET MVC 5 app results in OpenIdConnectProtocolValidator issue

I have an ASP.NET MVC 5 app that authenticates against Azure Active Directory. I wanted to enable SSL on it across the app. and hence leveraged global filters as follows:
public class FilterConfig
{
/// <summary>
/// Registers the global filters.
/// </summary>
/// <param name="filters">The filters.</param>
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequireHttpsAttribute());
}
}
After this I also set 'Enable SSL' in the project's properties to true. This gave me the following SSL URL -> https://localhost:34567. I updated the project to have this in its IIS Express path under the 'Web Tab' under Servers in 'Project URL'. However on running the site I run in to the following error:
IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'.
I have auth. enabled on the site. I use Azure Active directory.
The security code is as follows:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = audience,
Tenant = tenant,
});
The auth. values are being read from the web.config and are as follows:
<add key="ida:ClientId" value="<some_guid>" />
<add key="ida:Audience" value="https://localhost:34567/" />
<add key="ida:AADInstance" value="https://login.windows.net/{0}" />
<add key="ida:Tenant" value="microsoft.onmicrosoft.com" />
<add key="ida:PostLogoutRedirectUri" value="https://localhost:34567/" />
I tried setting RequireNonce to false as directed in the error message as follows:
ProtocolValidator = new OpenIdConnectProtocolValidator
{
RequireNonce = false
}
But this just resulted in an invalid request error.
Could someone help me understand what the problem is here? Everything worked great until SSL was enabled.
You can ignore exceptions if the error message starts with OICE_20004 or contains IDX10311. Note: do it on your own risk.
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
// Ensure the URI is picked up dynamically from the request;
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Uri.PathAndQuery;
context.ProtocolMessage.RedirectUri = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Uri.PathAndQuery;
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
if (context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311"))
{
context.SkipToNextMiddleware();
return Task.FromResult(0);
}
return Task.FromResult(0);
},
}
From the Azure management portal, check that your application under the corresponding active directory has the same Sign On URL and reply URL.
If they are not same, you will get this error.
This happens when you enable SSL because it changes only the sign on URL to the HTTPS URL while the reply URL remains the same HTTP URL.
Edit:
Read on if you want to know exactly why this is happening,
When you try to access your app using the https URL, it sets a cookie with a unique number(nonce) in your browser and hits Azure AD for authentication. After authentication, the browser has to give access to that cookie. But since the sign on URL and reply URL are different the browser does not recognise your app and does not give access to that cookie and hence the application throws this error.
I can reproduce this error by pressing back button couple of times on my web application, even after successful login.
can you try these 2 things:
in your code below:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = mViewWebSite.ClientId,
Authority = mViewWebSite.Authority,
PostLogoutRedirectUri = mViewWebSite.PostLogoutRedirectUri
});
add protocol validator as on of the authentication options, as what error suggest:
ProtocolValidator = new Microsoft.IdentityModel.Protocols.OpenIdConnectProtocolValidator(){
RequireNonce = false
}
or add notification, by this you can catch this error and redirect it to some error page. I do that to make it graceful. Until Katana people fixes it.
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error.aspx?message=" + context.Exception.Message);
return Task.FromResult(0);
}
},
I manage to work around this problem using following method in the Global.asax file. At least this won't show the exception to the client. I use ELMAH to catch exceptions.
protected void Application_Error(object sender, EventArgs args)
{
var ex = Server.GetLastError();
if (ex.Message.Contains("IDX10311:"))
{
Server.ClearError();
Response.Redirect("https://www.yoursitename.com");
}
Well it would probably be best to look at the katana source code, from that i found the exception type to be OpenIdConnectProtocolInvalidNonceException so i handle it like this.
if (n.Exception is OpenIdConnectProtocolInvalidNonceException &&
n.OwinContext.Authentication.User.Identity.IsAuthenticated)
{
n.SkipToNextMiddleware();
return;
}
I have this exception popup on browsers that cache the pages and users that click the back button after login.
The issue here is simple... took me hours to figure this out.
Since I was testing on my local had no https and to tell you the truth when initially creating my app in Azure AD since i wasnt expecting it to be https during my test I made it plain http (replyUrl's HomePage Url, Logout all that jazz)
Then after doing this i encountered the infinate loop issue a lot of people are getting. so then i decided to mock the cert on my local and yep that got rid of the infinate redirect but then brought another one the "IDX10311: RequireNonce is 'true' " one
Long story short... make your AzureAD App https in all its endpoints. and wallah!
#zb3b answer + #jonmeyer answer:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
...
Notifications = new OpenIdConnectAuthenticationNotifications()
{
...
AuthenticationFailed = (context) =>
{
if ((context.Exception is OpenIdConnectProtocolInvalidNonceException) &&
(context.OwinContext.Authentication.User.Identity.IsAuthenticated))
{
context.SkipToNextMiddleware();
return Task.FromResult(0);
}
return Task.FromResult(0);
},
...
}
});
Just adding another case I just ran into: the network you connect to may be modifying HTML content.
A customer called with an issue: he could not get past this error. It was a new laptop where he had not logged on before. After about one hour of trying several possible solutions, I decided to check the network he was connected to.
It turns out he was connected to a network in an airport, open and unsecured, and not using a VPN service (lacking some SETA there). I don't know exactly who operated that network or what they were doing, but the Azure AD service must have detected some type of tampering with the nonce.
The moment the user connected to a trusted network, the issue was resolved.

JWT Handler for .NET 4.5 throws error: GetIssuerName with single parameter is not supported

I've just implemented this JSON Web Token Handler for .Net 4.5 library described here:
http://www.cloudidentity.com/blog/2012/11/20/introducing-the-developer-preview-of-the-json-web-token-handler-for-the-microsoft-net-framework-4-5-2/
I have an ACS service that's supposed to be returning a JWT token to my application. However, at the point when I should be getting the token returned to my app (when the browser is redirected to my relying party's return uri), I instead get this exception:
WIF10200: GetIssuerName with single parameter is not supported. Call:
'GetIssuerName( SecurityToken securityToken, string issuer )'.
I don't think this is a problem directly with my code; because if I do a solution-wide search, "GetIssuerName" is not in my solution. I think it might have something to do with the certificate issuers WIF wants to trust, but I've followed the instructions in the article I linked above about importing the certificate from the ACS site's FederationMetadata... and I'm utterly stumped on this one.
Strangely enough, when I change my Relying Party token type from JWT to SAML 1 or 2, my relying party's Return Uri is never hit, I'm just redirected back to my realm error free. I would expect to finally land on my Return Uri. If someone could explain either phenomenon to me, it would help tremendously!
UPDATE
The GetIssuerName method in .NET 4.5 is part of the System.IdentityModel.Tokens.ValidatingIssuerNameRegistry class (there's also a System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry class with the same method, but that's not the one I'm working with).
From looking at the metadata, there are 2 overridden methods with this name in the class:
public override string GetIssuerName(SecurityToken securityToken);
public override string GetIssuerName(SecurityToken securityToken, string requestedIssuerName);
The error I'm getting from WIF seems to be coming directly from the first method listed there, as you can see from the stack trace below. With my relying party returning JWT, something somewhere is supposed to be calling the second method, whose signature has a second parameter, but is calling the first. I wish I knew how to change it.
[NotSupportedException: WIF10200: GetIssuerName with single parameter
is not supported. Call: 'GetIssuerName( SecurityToken securityToken,
string issuer )'.]
System.IdentityModel.Tokens.ValidatingIssuerNameRegistry.GetIssuerName(SecurityToken
securityToken) +156
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateIssuer(JWTSecurityToken
jwt, TokenValidationParameters validationParameters) +1303
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateToken(JWTSecurityToken
jwt, TokenValidationParameters validationParameters) +278
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateToken(SecurityToken
token) +2248
System.IdentityModel.Tokens.SecurityTokenHandlerCollection.ValidateToken(SecurityToken
token) +135
System.IdentityModel.Services.TokenReceiver.AuthenticateToken(SecurityToken
token, Boolean ensureBearerToken, String endpointUri) +502
System.IdentityModel.Services.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequestBase
request) +1508
System.IdentityModel.Services.WSFederationAuthenticationModule.OnAuthenticateRequest(Object
sender, EventArgs args) +700
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
+80 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165

WIF- ID1014: The signature is not valid. The data may have been tampered with

I've been using WIF to authenticate our new website, the STS is based upon the starter-sts implementation.
To enable this to work correctly on out load balanced environment I've used the following in the global.asax to override the default certificate behaviour.
void onServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
List<CookieTransform> sessionTransforms = new List<CookieTransform>(new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(e.ServiceConfiguration.ServiceCertificate)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
}
This is all working just find and people have been successfully using the system, however every now and then we get a blast of :
ID1014: The signature is not valid. The data may have been tampered with.
in the event logs, so I switched on WIF tracing and saw the following mentioned in the log.
ID1074: A CryptographicException occurred when attempting to encrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false.
I have a feeling this is leading me down a dark alley as I thought because I'd changed the implementation to use RSA this shouldn't affect me.
Any ideas to help me?
The browser cookies are encrypted with "old" mechanism - DPAPI.
Therefore, when the server tries to decrypt the cookies, it fails - your code use RSA now, not DPAPI.
As a workaround, clear the browser cache, and the application will start running as expected.
I changed the implementation to amend the timeout in the ontokencreated method. This prevents the reissue.
protected override void OnSessionSecurityTokenCreated(Microsoft.IdentityModel.Web.SessionSecurityTokenCreatedEventArgs args)
{
args.SessionToken = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(
args.SessionToken.ClaimsPrincipal,
args.SessionToken.Context,
DateTime.UtcNow,
DateTime.UtcNow.AddDays(365),
true
);
//base.OnSessionSecurityTokenCreated(args);
}
Did you try setting the loadUserProfile option to true? Does the problem still occur?
(Select the Application pool in IIS and then click "Advanced Settings" on the right. "Load User Profile" is in the "Process Model" section).
The intermittent occurrence of your error, combined with the DPAPI exception showing up in your traces suggests to me that you aren't actually overriding the cookie transform, and your service is still using DPAPI.
This might be a long shot, but in your code snippet I noticed your method override "onServiceConfigurationCreated" starts with a lower case o. Such a typo would indeed prevent you from properly overriding default WIF behavior.

Implementing Kerberos authentication with Javamail

There is an older thread that seems to be the only relevant discussion I have been able to find.
I am trying to implement Kerberos with Javamail (over IMAP) and I have gotten my self thoroughly confused on exactly what is to be done with mail.imap.sasl.mechanisms. Assume I give the value "GSS-API" but am kind of lost where to go from there. I notice that Javamail has an class IMAPSaslAuthernticator. It seems to me that this is what is needed but I can find precious little documentation on where or how to use it.
Any ideas?
NOTE: I wanted to post more code for my question, but according the site directions, full posts are only for answers. So, I have edited the code I originally posted question.
Below is the real meat. For now, once I pass this point I get the Message[] from the server and print the size to console.
SSL/TLS security is required so it is enabled below. In this example certificates are managed by a trusted keystore in Java.
private Folder folder;
private Session session;
private Store store;
public boolean connectToKerberosMail() {
if (folder != null && folder.isOpen()) {
return true;
}
Properties properties = new Properties();
properties.setProperty("mail.debug", "true");
properties.put("mail.imaps.connectiontimeout",600000);
properties.put("mail.imaps.timeout",601000);
properties.put("mail.imaps.fetchsize", 65000);
properties.put("mail.imaps.starttls.enable", "true");
properties.put("mail.imaps.starttls.required", "false");
properties.put("mail.imaps.sasl.enable","true");
properties.put("mail.imaps.sasl.mechanisms","GSSAPI");
properties.put("mail.imaps.sasl.authorizationid",<user>);
properties.put("mail.imaps.sasl.realm",<realm>);
System.setProperty( "sun.security.krb5.debug", "true");
System.setProperty( "java.security.krb5.realm",<realm>);
System.setProperty( "java.security.krb5.kdc", <ip-address>);
System.setProperty( "java.security.auth.login.config", "jaas.conf");
System.setProperty( "javax.security.auth.useSubjectCredsOnly", "false");
try {
session = Session.getInstance(properties);
} catch (Exception e) {
session = null;
return false;
}
session.setDebug(true);
URLName url = new URLName("imaps", <host>, <port>, "", <user>, <pass>);
store = new IMAPSSLStore(session, url);
try {
store.connect();
} catch (Exception e) {
e.printStackTrace();
store = null;
session = null;
return false;
}
return openFolder();
}
My jaas.conf file is as follows (the ticket cache was acquired from kinit):
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
I recently posted here the output but noticed that some of my properties where designated "imap" instead of "imaps". So I am doing more testing before posintg output incase it changes.
In the mean time is what I have above correct? From what I understand I have to enable imap for the imap connection, startTLS for the TLS/SSL, and sasl for kerberos. But maybe something is overriding the another?
While not 100% the way there yet i made some discoveries. LOGIN was happening with the protocol in the NamedURL was "imap". I changed it to "imaps".
However, it look like javamail takes the protocol and host uses them to contruct the principal. protocol/host#realm? so I was applying to principal imaps/host#REALM which didnt exist so failed on a non-matching pricipals error.
So, we added this new principal to the servers and got past this.
But authentication is still failing. In the kerberos log I was approved and sent a ticket for accessing the mail. But I do not see it in my ticket cache (using klist) only the first ticket for accessing kerberos (I got from using kinit).
It seems that I say this in every response. I don't know how to get the word out....
You almost certainly want to change Session.getDefaultInstance() to Session.getInstance(), although that's probably not the source of your problems.
Anyway, what does the protocol trace show when you run your program? (emailSession.setDebug(true);)
I don't know enough about Kerberos, and especially how Kerberos works as a SASL mechanism, but aren't you going to have to specify some sort of password? Or can it get the appropriate Kerberos ticket without asking you to prove who you are?

DotNetOpenAuth: Message signature was incorrect

I'm getting a "Message signature was incorrect" exception when trying to authenticate with MyOpenID and Yahoo.
I'm using pretty much the ASP.NET MVC sample code that came with DotNetOpenAuth 3.4.2
public ActionResult Authenticate(string openid)
{
var openIdRelyingParty = new OpenIdRelyingParty();
var authenticationResponse = openIdRelyingParty.GetResponse();
if (authenticationResponse == null)
{
// Stage 2: User submitting identifier
Identifier identifier;
if (Identifier.TryParse(openid, out identifier))
{
var realm = new Realm(Request.Url.Root() + "openid");
var authenticationRequest = openIdRelyingParty.CreateRequest(openid, realm);
authenticationRequest.RedirectToProvider();
}
else
{
return RedirectToAction("login", "home");
}
}
else
{
// Stage 3: OpenID provider sending assertion response
switch (authenticationResponse.Status)
{
case AuthenticationStatus.Authenticated:
{
// TODO
}
case AuthenticationStatus.Failed:
{
throw authenticationResponse.Exception;
}
}
}
return new EmptyResult();
}
Working fine with Google, AOL and others. However, Yahoo and MyOpenID fall into the AuthenticationStatus.Failed case with the following exception:
DotNetOpenAuth.Messaging.Bindings.InvalidSignatureException: Message signature was incorrect.
at DotNetOpenAuth.OpenId.ChannelElements.SigningBindingElement.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\ChannelElements\SigningBindingElement.cs:line 139
at DotNetOpenAuth.Messaging.Channel.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\Messaging\Channel.cs:line 992
at DotNetOpenAuth.OpenId.ChannelElements.OpenIdChannel.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\ChannelElements\OpenIdChannel.cs:line 172
at DotNetOpenAuth.Messaging.Channel.ReadFromRequest(HttpRequestInfo httpRequest) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\Messaging\Channel.cs:line 386
at DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.GetResponse(HttpRequestInfo httpRequestInfo) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\RelyingParty\OpenIdRelyingParty.cs:line 540
Appears that others are having the same problem: http://trac.dotnetopenauth.net:8000/ticket/172
Does anyone have a workaround?
Turns out this was an issue with using DotNetOpenAuth in a web farm environment.
When you create your OpenIdRelyingParty make sure you pass null in the constructor.
This puts your web site into OpenID stateless or 'dumb' mode. It's slightly slower for users to log in (if you even notice) but you avoid having to write an IRelyingPartyApplicationStore to allow DotNetOpenAuth to work across your farm;
var openIdRelyingParty = new OpenIdRelyingParty(null);
All this discussion revolves around the following question:
How does Relying Party (RP) make sure the request containing the authentication token is coming from the OP(OpenId Provider ) to which he forwarded the user’s request to?
Following steps explains how it happens
User Request comes to the Replying Party (RP), our website in our case
Application stores a unique signature corresponding to this user in a local signature store (LSS) and then embeds this signature in the Message and forward this Message to OpenId Provider(OP)
User types his credentials and the OP authenticates his Message and then forwards this Message, which has the signature still embedded in it, back to RP
RP compare the signature which is embedded in the Message to the signature which is in LSS and if they match RP authenticate the user
If the LSS vanishes (somehow) before the Message comes back from OP there is nothing for RP to compare the signature with thus it fails to authenticate user and throws error: Message signature was incorrect.
How can LSS Vanish:
ASP.net refreshes the application pool
IIS is restarted
In web farm the Message is served by application hosted on different server
Two solutions to this issue:
RP run’s in dumb mode
a. It does not store and signature locally and thus does not use signature comparison to make sure the Message is coming from the OP to which he forwarded the user to for authentication
b. Instead, once RP received the authentication Message from the OP it send the Message back to OP and ask him to check if he is the one who has authenticate this user and is the originator of the Message. If OP replies Yes I am the originator of this Message and I have created this message then the user is authenticated by RP
Implement your own persistence store that does not vanish, not matter what ASP.net does to the process, much like using SQL to store session state.
We fixed this issue by implementing IRelyingPartyApplicationStore (IOpenIdApplicationStore in newer versions of DotNetOpenAuth) and adding the store class name to the .config
<dotNetOpenAuth>
<openid ...>
<relyingParty>
...
<store type="some.name.space.MyRelyingPartyApplicationStore, some.assembly"/>
</relyingParty>
</openid>
...
</dotNetOpenAuth>
The interface is a composition of two other interfaces with five members all together.
/// <summary>
/// A hybrid of the store interfaces that an OpenID Provider must implement, and
/// an OpenID Relying Party may implement to operate in stateful (smart) mode.
/// </summary>
public interface IOpenIdApplicationStore : ICryptoKeyStore, INonceStore
{
}
We used dumb mode as a quick fix to get up an running, but in the end you'll probably want something like this.

Resources