IAuthenticationResponse.GetExtension<ClaimsResponse>() always returning null - c#

Update
Thanks to a comment by #IvanL, it turns out that the problem is Google specific. I have since tried other providers and for those everything works as expected. Google just doesn't seem to send claims information. Haven't yet been able to figure out why or what I need to differently to get Google to send it.
A wild stab in the dark says it may be related to the realm being defaulted to http://:/ as I have seen an answer by Andrew Arnott that Google changes the claimed identifier for the same account based on the realm passed with the authentication request.
Another possibly important tidbit of information: unlike many of the examples that can be found around the web for using dotnetopenauth, I am not using a "simple" textbox and composing the openIdIdentifier myself, but I am using the openID selector and that is providing the openIdIdentifier passed to the ValidateAtOpenIdProvider. (As per the Adding OpenID authentication to your ASP.NET MVC 4 application article.)
Question is: why is IAuthenticationResponse.GetExtension() always returning null when using Google as the openId provider, when otherwise all relevant gotcha's with regard to Google (Email requested as required, AXFetchAsSregTransform, etc) have been addressed?
Original
I am struggling with getting DotNetOpenAuth to parse the response returned from the provider. Followed the instructions of Adding OpenID authentication to your ASP.NET MVC 4 application up to the point where the login should be working and a login result in a return to the home page with the user's name (nick name) displayed at the top right. (That is up to "The user should at this point see the following:" just over half way down the article).
I am using Visual Studio Web Developer 2010 Express with C#. DotNetOpenAuth version is 4.0.3.12153 (according to the packages.config, 4.0.3.12163 according to Windows Explorer).
My web.config was modified following the instructions in Activating AXFetchAsSregTransform which was the solution for DotNetOpenId - Open Id get some data
Unfortunately it wasn't enough to get it working for me.
The openid-selector is working fine and resulting in a correct selection of the openid provider. The authentication request is created as follows:
public IAuthenticationRequest ValidateAtOpenIdProvider(string openIdIdentifier)
{
IAuthenticationRequest openIdRequest = openId.CreateRequest(Identifier.Parse(openIdIdentifier));
var fields = new ClaimsRequest()
{
Email = DemandLevel.Require,
FullName = DemandLevel.Require,
Nickname = DemandLevel.Require
};
openIdRequest.AddExtension(fields);
return openIdRequest;
}
This all works. I can login and authorize the page to receive my information, which then results in a call to GetUser:
public OpenIdUser GetUser()
{
OpenIdUser user = null;
IAuthenticationResponse openIdResponse = openId.GetResponse();
if (openIdResponse.IsSuccessful())
{
user = ResponseIntoUser(openIdResponse);
}
return user;
}
openIdResponse.IsSuccessful is implemented as an extension method (see linked article):
return response != null && response.Status == AuthenticationStatus.Authenticated;
and always is successful as the ResponseIntoUser method is entered:
private OpenIdUser ResponseIntoUser(IAuthenticationResponse response)
{
OpenIdUser user = null;
var claimResponseUntrusted = response.GetUntrustedExtension<ClaimsResponse>();
var claimResponse = response.GetExtension<ClaimsResponse>();
// For this to work with the newer/est version of DotNetOpenAuth, make sure web.config
// file contains required settings. See link for more details.
// http://www.dotnetopenauth.net/developers/help/the-axfetchassregtransform-behavior/
if (claimResponse != null)
{
user = new OpenIdUser(claimResponse, response.ClaimedIdentifier);
}
else if (claimResponseUntrusted != null)
{
user = new OpenIdUser(claimResponseUntrusted, response.ClaimedIdentifier);
}
else
{
user = new OpenIdUser("ikke#gmail.com;ikke van ikkenstein;ikke nick;ikkeclaimedid");
}
return user;
}
My version above only differs from the code in the linked article by my addition of the final else block to ensure that I always get the home page with a user name and a logoff link displayed (which helps when trying to do this several times in succession).
I have tried both Google and Yahoo. Both authenticate fine, both return an identity assertion as logged by the WebDev server. However, GetUntrustedExtenstion and GetExtension always return null. I always get to see "ikke nick" from the last else, never the name I actually used to authenticate.
I am at a loss on how to continue to try and get this to work. It probably is some oversight on my part (I am an experienced developer but just started dipping my toes in C# and web front-end development), and I can't see it.
Any and all suggestions on how to proceed / debug this are very much welcome.

Are you using Google as OpenId provider to test your solution against? Because Google has/had the habit of including the Claims only the first time you authenticate the application. So perhaps try using a fresh google account and see if that works?
Sorry for the slow response, doing a big migration at a client this week :-) Glad that this little comment resolved your issue.

Related

Simplified LDAP/AD Server on C#

I've searched without much success to the simplest (and yet working) example of an LDAP/AD Server for C#. Many libraries exist to connect to LDAP servers, but not the LDAP Server by itself (on C#).
I found however some information about it and even a post requesting a simple LDAP server that was answered "LDAP isn't simple"; and yet i read a lot of the RFC4511 and this sample code at GitHub Flexinet LDAP Server, but unfortunatly i don't have yet the knowledge to complete it's code.
My goal is not to make a fully functional LDAP server, but one that can at least do:
Serve as a login pool for softwares that allow it's users to be
registered and log on a AD/LDAP server (just check for login and
password for authentication).
Allow softwares like Outlook and Thunderbird to get a list of users (without passwords) with first and last name, e-mail address, phone number and department for contact list model.
No delete, add (or create), move, and other
functions are required since the main software that i aim to
integrate it with will do all the user and group management.
UPDATE
I'm trying to implement the Flexinet sample and adjust to that functionalities; as form of a question what should i do to change this function to prevent it from causing an exception (on the "var filter = searchRequest.ChildAttributes[6];" line it always breaks) when i call from a LDAP client software:
private void HandleSearchRequest(NetworkStream stream, LdapPacket requestPacket)
{
var searchRequest = requestPacket.ChildAttributes.SingleOrDefault(o => o.LdapOperation == LdapOperation.SearchRequest);
var filter = searchRequest.ChildAttributes[6];
if ((LdapFilterChoice)filter.ContextType == LdapFilterChoice.equalityMatch && filter.ChildAttributes[0].GetValue<String>() == "sAMAccountName" && filter.ChildAttributes[1].GetValue<String>() == "testuser") // equalityMatch
{
var responseEntryPacket = new LdapPacket(requestPacket.MessageId);
var searchResultEntry = new LdapAttribute(LdapOperation.SearchResultEntry);
searchResultEntry.ChildAttributes.Add(new LdapAttribute(UniversalDataType.OctetString, "cn=testuser,cn=Users,dc=dev,dc=company,dc=com"));
searchResultEntry.ChildAttributes.Add(new LdapAttribute(UniversalDataType.Sequence));
responseEntryPacket.ChildAttributes.Add(searchResultEntry);
var responsEntryBytes = responseEntryPacket.GetBytes();
stream.Write(responsEntryBytes, 0, responsEntryBytes.Length);
}
var responseDonePacket = new LdapPacket(requestPacket.MessageId);
responseDonePacket.ChildAttributes.Add(new LdapResultAttribute(LdapOperation.SearchResultDone, LdapResult.success));
var responseDoneBytes = responseDonePacket.GetBytes();
stream.Write(responseDoneBytes, 0, responseDoneBytes.Length);
}
The code is on the github link.
Finally i made a fork of the Flexinet LDAP Server on #Sammuel-Miranda/LdapServerLib and with the author's support and some changes and adaptations i completed this implementation. It responds to the bind and search calls and works perfectly for Outlook and Thunderbird to use as a shared address book.
I did not implemente however any ADD/MODIFY/DELETE request (but would not be hard to do) since i don't need then.
I found on the RFC4511 the explanation on how the search works ... and i'm "kind" of understanding it, not very well - and i see that the method implemented on the GitHub from Flexinet LDAP Server only answer to bind and search requests of one single user (since it's only a example implementation).
The client is requesting diferent calls to verify capabilities, structure and other info before making the search request itself. So i'll implement it all, one by one.
Still, if any other lib (in C#) exists, and anyone know about, would be better than writing a hole new server. If my implementation works, i'll fork it on github and share.

ProviderName returns empty string for google login under .Net Framework 4.0 Webform

I'm trying to authenticate web app using Google sign-in option. I have tried with below options ( as I don't want to use .net 4.0> frameworks)
.Net Framework 4.0
Webforms
Option 1:
OpenAuth.AuthenticationClients.AddGoogle();
This is default option provided under AuthConfig.cs
To get Google login option I have uncommented the above line
Giving Error as Sequence contains no elements. No OpenID endpoint found.
Option 2 :
var client = new GoogleOAuth2Client("clientId", "secretId");
var extraData = new Dictionary<string, string>();
OpenAuth.AuthenticationClients.Add("Google", () => client, extraData);
If I try with above option, when I click on google login, it is taking me to Google Sign in Page, after successful login, while request coming back to the application, OpenAuth.GetProviderNameFromCurrentRequest(); returns empty string. as per the below condition, it is taking me to the login page again. For Facebook and Twitter login, it is returning respective provider name except for the google.
ProviderName = OpenAuth.GetProviderNameFromCurrentRequest();
if (String.IsNullOrEmpty(ProviderName))
{
Response.Redirect(FormsAuthentication.LoginUrl);
}
What is the reason behind it and what is the solution?
If you went with option 2 you need to change RegisterExternalLogin.aspx to the following
{GoogleOAuth2Client.RewriteRequest();
ProviderName = OpenAuth.GetProviderNameFromCurrentRequest();}
This will allow the ProviderName to be found.
You also need to add {using DotNetOpenAuth.GoogleOAuth2;}

DNN Mangled Username When Using Custom Registration Form

History
We have an elaborate system which ties into DNN on multiple levels for custom user information. Our users are governed under a completely different database than DNN. Because of this, we have special requirements and have created our own membership provider to our database and registration form for our specific needs.
The Problem
In the past, we have used our membership provider and registration form with no problems. Recently, we have encountered a strange situation where the username is mangled after someone attempts to register a new account. It is mangled in such a way that the username becomes the user's Region followed by a hyphen, followed by the user's email address in full. As an example, if a user fills out the form with the following:
username: user
region: CA
email: me#example.com
Before UserController.CreateUser(user) is called, the userInfo.Username equals "user". However, after the call completes, it will be "CA-me#example.com" and the created record in the User table will reflect this change.
Using my custom membership provider (which calls the default AspNetMembershipProvider) verifies that the issue seems to be with AspNetMembershipProvider's CreateUser(ref userInfo); Before this call, the username is "user", but after this call completes it becomes "CA-me#example.com". The result of the call is Success and the user is created in the database. So it doesn't appear to be a failure that is causing the issue.
Settings
I'm not sure if there are settings on DNN which may cause this problem. In my development environment, this entire thing is a non-issue. It only occurs in the live and a secondary internal test environment. Since I'm not able to reproduce this bug in my debug environment, I've considered this might be a site configuration issue. The only option I've found on Site Settings is to make the username the same as the email address, but this is disabled.
I'm going to post my membeship provider code for updating the DNN record, and I'll post the code which creates the user in my register module. I don't know if anyone might need additional code, but I'm willing to provide more where necessary.
Membership Provider
/// <summary>
/// Updates the user in the DNN database.
/// </summary>
/// <param name="userInfo"></param>
private void UpdateDnn(UserInfo userInfo)
{
// _defaultProvider is an instance of AspNetMembershipProvider
if (_defaultProvider.GetUser(userInfo.PortalID, userInfo.UserID) != null)
_defaultProvider.UpdateUser(userInfo);
else
_defaultProvider.CreateUser(ref userInfo);
}
Register Module
public DotNetNuke.Security.Membership.UserCreateStatus Register(Inputs.RegistrationInput input)
{
var userInfo = new UserInfo();
input.Fill(portalId, userInfo); // This copies the user input into the userInfo object.
return UserController.CreateUser(ref userInfo);
}
RegistrationInput.Fill()
public void Fill(int portalId, UserInfo userInfo)
{
if (userInfo == null) throw new ArgumentNullException("userInfo");
userInfo.PortalID = portalId;
userInfo.FirstName = FirstName;
userInfo.LastName = LastName;
userInfo.Username = UserName;
userInfo.DisplayName = DisplayName;
userInfo.Email = Email;
userInfo.Username = UserName;
userInfo.Membership.Password = Password;
userInfo.Profile.Street = Street;
userInfo.Profile.Unit = SuiteApt;
userInfo.Profile.Region = State;
userInfo.Profile.City = City;
userInfo.Profile.PostalCode = Zip;
userInfo.Profile.Telephone = PrimaryPhone;
}
Update
I was digging around in DDN's core for a work around and stumbled upon this (AspNetMembershipProvider.cs:870):
// Check that the OAuth service currently being used for login is the same as was previously used (this should always be true if user authenticated to userid)
if (authUser == null || authUser.AuthenticationType.Equals(service, StringComparison.OrdinalIgnoreCase))
{
isOAuthUser = true;
//DNN-4133 Change username to email address to ensure multiple users with the same email prefix, but different email domains can authenticate
user.Username = service + "-" + user.Email;
}
else
{
createStatus = UserCreateStatus.DuplicateEmail;
}
The line user.Username = service + "-" + user.Email is in the exact format of the username that I am receiving after I call UserController.CreateUser(). The question now is: is this an error in my code or their code? I'm going to keep digging into this, and if nobody answers, I'll try to post an answer to this problem after I understand what is happening here. Furthermore, if I find this to be a bug in their code, I'll post a bug report on DNN's bug tracker and link back to the bug page here.
I finally found the problem. It turns out that the issue is with a line of code above the posted code in the membership provider in the same function:
string service = HttpContext.Current.Request.Params["state"];
The problem with this line is that I pass state via POST as the user's state of residency. DNN seems to consider it the service state. The solution to this is to change the POST input parameter name from state to something else - preferably region as DNN recognizes states as regions. An easy fix, all-in-all, but it took me quite a while to find it. I think DNN should have allowed this parameter to be changed via the state of the membership object or parameter rather than grabbing it directly from the request, but there's not much that can be done on our end in that regard (modifying the core source is strongly discouraged).

LinqToTwitter Error - The remote certificate is invalid according to the validation procedure

I've found several different questions about this error, but none of them seem to outline my scenario.
I am creating a website that pulls in tweets from our company's Twitter account, and displaying them on a social wall. I am using C# asp.NET webforms. The C# code uses a Linqtotwitter library to handle the authentication and the "tweet pulling." It grabs the tweets and dumps them onto an aspx file as a big long string of json. We then have a jquery script that reads through the json and displays the tweets on the page nice and pretty like.
The code currently works perfect on my dev box. But when I push the code up to production I get this .NET error:
The remote certificate is invalid according to the validation procedure
I'll provide my code in a bit here, but first let me give you a little background. I have no idea if this information would be relevant or not, but who knows. This website is actually part of a larger project to fit several tiny one page microsites that we get from marketing onto one server to reduce the overhead they cause. These microsites can all have a different host name, but they point to the same IP address. An httpmodule lives on that server, and intercepts all requests coming in, and redirects them to an appropriate sub folder depending on the host name.
From the research that I've done, it seems that SSL is tied into this error quite a bit. I'm still pretty new to the IT world, and I'm learning more about SSL as this troubleshooting goes on. The server these microsites live on does have a few SSL certificates on it, and one of the microsites uses SSL, but not the website I'm currently working on. But since they both share the same IP address in that sense they kind of ARE the same website.
This is the C# LinqtoTwitter code:
private SingleUserAuthorizer auth;
private TwitterContext twitterCtx;
protected void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "application/json";
auth = new SingleUserAuthorizer
{
Credentials = new SingleUserInMemoryCredentials
{
ConsumerKey =
ConfigurationManager.AppSettings["twitterConsumerKey"],
ConsumerSecret =
ConfigurationManager.AppSettings["twitterConsumerSecret"],
TwitterAccessToken =
ConfigurationManager.AppSettings["twitterAccessToken"],
TwitterAccessTokenSecret =
ConfigurationManager.AppSettings["twitterAccessTokenSecret"]
}
};
if (auth.IsAuthorized)
{
twitterCtx = new TwitterContext(auth);
var tweetResponse =
(from tweet in twitterCtx.Status
where tweet.Type == StatusType.User &&
tweet.ScreenName == "OurProfile" &&
tweet.IncludeRetweets == true
select tweet)
.ToList();
Results.Text = twitterCtx.RawResult;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (twitterCtx != null)
{
twitterCtx.Dispose();
twitterCtx = null;
}
}
Does anyone have any ideas of what could be the problem here? Like I said, I'm still pretty new, and I'm at a loss here even how to troubleshoot this issue beyond Google. Could it be something where our server can't verify that Twitter's SSL certificate is from a trusted source? Let me know if I can provide any more information or any more code. Thanks for your time and for reading through my post!

PayPal SOAP API - Express Checkout - Error 10002

I'm trying to connect my website to the Paypal Sandbox in order to use the Express Checkout feature. I've used this link as reference but i keep getting the 10002 Error "Security header is not valid".
From the documentation this has to be a invalid credentials problem but if i made the request manually through soapUI it returns "Sucess", if i use the curl command it also works as expected.
Scenario: ASP.NET page with two Web References one to https://www.sandbox.paypal.com/wsdl/PayPalSvc.wsdl and another to https://www.paypalobjects.com/wsdl/PayPalSvc.wsdl, the given credentials are Username, Password and Signature as you can see in the following code snippet:
using CloudShop.com.paypal.sandbox.www;
namespace CloudShop
{
public static PayPalAPIAASoapBinding BuildPayPalWebservice()
{
UserIdPasswordType credentials = new UserIdPasswordType()
{
Username = CloudShopConf.PayPalAPIUsername,
Password = CloudShopConf.PayPalAPIPassword,
Signature = CloudShopConf.PayPalAPISignature
};
PayPalAPIAASoapBinding paypal = new PayPalAPIAASoapBinding();
paypal.RequesterCredentials = new CustomSecurityHeaderType()
{
Credentials = credentials
};
return paypal;
}
Right now i would like to know how to proceed with the debug. What could be wrong?
Some ideas:
Check if you are using the Live-Credentials for the sandbox account.
Are you using https://api-3t.sandbox.paypal.com/2.0/ (especially the -3t part) as the endpoint? You should as you are using Signature authentication.
As usual, you should step through every setting you are using: protocol, API Endpoint, Version, Credentials etc. and compare you're manual SoapUI call with the information stored in you shop configuration.
I also found a blog article on this error that might help resolving this issue.

Categories

Resources