LDAP Authentication with Asp.NET Identity - asp.net

I trying impliment Active Directory authentication for my ASP.NET MVC application. I use System.DirectoryServices and during login find user in UserManager. If user not found I'm trying find user in Active Directory and if successful register user in asp.net mvc app with UserManager.CreateAsync().
private ApplicationUserManager _userManager;
private ApplicationRoleManager _roleManager;
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel loginModel, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(loginModel.UserName, loginModel.Password);
if (user != null)
{
await SignInAsync(user, loginModel.RememberMe);
return RedirectToLocal(returnUrl);
}
string userFullName;
if (AuthenticateActiveDirectoryUser("mydomain.local", loginModel.UserName, loginModel.Password, out userFullName))
{
var newUser = new ApplicationUser { UserName = loginModel.UserName, FullName = userFullName };
var result = await UserManager.CreateAsync(newUser, loginModel.Password);
if (result.Succeeded)
{
await SignInAsync(newUser, loginModel.RememberMe);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
else
{
ModelState.AddModelError("", "Invalid UserName or Password");
}
}
return View(loginModel);
}
private bool AuthenticateActiveDirectoryUser(
string domain,
string username,
string password,
out string fullName)
{
fullName = string.Empty;
var domainAndUsername = string.Format("{0}\\{1}", domain, username);
var ldapPath = "";
var entry = new DirectoryEntry(ldapPath, domainAndUsername, password);
try
{
// Bind to the native AdsObject to force authentication.
var obj = entry.NativeObject;
var search = new DirectorySearcher(entry) { Filter = "(SAMAccountName=" + username + ")" };
search.PropertiesToLoad.Add("cn");
var result = search.FindOne();
if (result == null)
return false;
try
{
fullName = (string)result.Properties["cn"][0];
}
catch
{
fullName = string.Empty;
}
}
catch (Exception ex)
{
return false;
}
return true;
}
But in my implementation ignored cases if user change password in Active Directory account or AD Account was deleted.
I can check it manually in my code, but maybe exists other ways in ASP.NET Identity to implement authentication by Active Directory user account?

see if this can help u
protected bool ActiveDirectoryLogin(string Username, string Password, string Domain)
{
bool Success = false;
//System.DirectoryServices.DirectoryEntry Entry =
// new System.DirectoryServices.DirectoryEntry("LDAP://***.**.**.**:389/cn=***-People,o=**,dc=**,dc=edu,dc=sa", "uid=" + Username + ",cn=***-People,o=***,dc=***,dc=edu,dc=sa", Password, AuthenticationTypes.None);
System.DirectoryServices.DirectoryEntry Entry =
new System.DirectoryServices.DirectoryEntry("LDAP://ldapmaster.***.edu.sa:389/cn=***-People,o=***,dc=***,dc=edu,dc=sa", "uid=" + Username + ",cn=***-People,o=***,dc=***,dc=edu,dc=sa", Password,AuthenticationTypes.None);
//System.DirectoryServices.DirectoryEntry Entry =
// new System.DirectoryServices.DirectoryEntry("LDAP://ldapmaster.***.edu.sa:389/cn=***-People,o=***,dc=***,dc=edu,dc=sa", Username , Password, AuthenticationTypes.None);
System.DirectoryServices.DirectorySearcher Searcher = new System.DirectoryServices.DirectorySearcher(Entry);
try
{
Object nat = Entry.NativeObject;
Success = true;
// System.DirectoryServices.SearchResult Results = Searcher.FindOne();
// Success = (Results != null);
}
catch (Exception e)
{
Success = false;
}
return Success;
}

Related

Confirmation email got Invalid token

I'm adding confirmation email feature to my ASP.NET WebAPI project. The server can send email fine, however, the confirmation link always return "Invalid token".
I checked some reasons as pointed out here
http://tech.trailmax.info/2015/05/asp-net-identity-invalid-token-for-password-reset-or-email-confirmation/
but it seems that none of them is the root cause
Below is my code:
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result;
result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
try
{
await userManager.AddToRoleAsync(user.Id, "Player");
//Generate email confirmation token
//var provider = new DpapiDataProtectionProvider("GSEP");
var provider = new MachineKeyProtectionProvider();
userManager.UserTokenProvider = new DataProtectorTokenProvider<GSEPUser>(provider.Create("EmailConfirmation"));
var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
code = System.Web.HttpUtility.UrlEncode(code);
EmailHelper emailHelper = new EmailHelper();
string callBackUrl = emailHelper.GetCallBackUrl(user, code);
EmailMessage message = new EmailMessage();
message.Body = callBackUrl;
message.Destination = user.Email;
message.Subject = "GSEP Account confirmation";
emailHelper.sendMail(message);
}
catch (Exception e)
{
return Ok(GSEPWebAPI.App_Start.Constants.ErrorException(e));
}
}
And now is EmailHelper
public class EmailHelper
{
public string GetCallBackUrl(GSEPUser user, string code)
{
var newRouteValues = new RouteValueDictionary(new { userId = user.Id, code = code });
newRouteValues.Add("httproute", true);
UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes);
string callbackUrl = urlHelper.Action(
"ConfirmEmail",
"Account",
newRouteValues,
HttpContext.Current.Request.Url.Scheme
);
return callbackUrl;
}
public void sendMail(EmailMessage message)
{
#region formatter
string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
string html = "Please confirm your account by clicking this link: link<br/>";
html += HttpUtility.HtmlEncode(#"Or click on the copy the following link on the browser:" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress("myemail#example.com");
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
SmtpClient smtpClient = new SmtpClient("smtp-mail.outlook.com", Convert.ToInt32(587));
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("myemail#example.com", "mypassword!");
smtpClient.Credentials = credentials;
smtpClient.EnableSsl = true;
smtpClient.Send(msg);
}
}
And 2 MachineKey class
public class MachineKeyProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new MachineKeyDataProtector(purposes);
}
}
public class MachineKeyDataProtector : IDataProtector
{
private readonly string[] _purposes;
public MachineKeyDataProtector(string[] purposes)
{
_purposes = purposes;
}
public byte[] Protect(byte[] userData)
{
return MachineKey.Protect(userData, _purposes);
}
public byte[] Unprotect(byte[] protectedData)
{
return MachineKey.Unprotect(protectedData, _purposes);
}
}
I also added machineKey tag in Web.config as some instruction pointed out.
And finally is my confirmation email API
[AllowAnonymous]
[HttpGet]
public async Task<IHttpActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return Ok("Confirm error");
}
IdentityResult result;
try
{
result = await UserManager.ConfirmEmailAsync(userId, code);
}
catch (InvalidOperationException ioe)
{
// ConfirmEmailAsync throws when the userId is not found.
return Ok("UserID not found");
}
if (result.Succeeded)
{
return Ok("Confirmation succesfully");
}
else
{
return Ok(result.Errors);
}
}
Please show me where am I go wrong
I know this is an old thread. But I though of adding the answer as it could help others.
You are using the below code
string callbackUrl = urlHelper.Action(
"ConfirmEmail",
"Account",
newRouteValues,
HttpContext.Current.Request.Url.Scheme
);
and the UrlHelper.Action already does the url encoding for you in the latest MVC versions. So here in your code you are doing the encoding twice (one inside the Register and another inside GetCallBackUrl using urlHelper.Action) and that is why you are getting the invalid token error.

How to get access token identity in asp.net webforms external login page?

I'm using the new Microsoft identity to manage my website login and register.
I've configured the website to start using external login (Facebook).
How i can get the Access Token in the (RegisterExternalLogin) page?
``
protected void Page_Load(){
// Process the result from an auth provider in the request
ProviderName = IdentityHelper.GetProviderNameFromRequest(Request);
if (String.IsNullOrEmpty(ProviderName))
{
RedirectOnFail();
return;
}
if (!IsPostBack)
{
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
if (loginInfo == null)
{
RedirectOnFail();
return;
}
var user = manager.Find(loginInfo.Login);
if (user != null)
{
IdentityHelper.SignIn(manager, user, isPersistent: false);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else if (User.Identity.IsAuthenticated)
{
Response.Write("ok.2");
// Apply Xsrf check when linking
var verifiedloginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo(IdentityHelper.XsrfKey, User.Identity.GetUserId());
if (verifiedloginInfo == null)
{
RedirectOnFail();
return;
}
var result = manager.AddLogin(User.Identity.GetUserId(), verifiedloginInfo.Login);
if (result.Succeeded)
{
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
AddErrors(result);
return;
}
}
else
{
try
{
var accessToken = "What can i get the access token";
Response.Write(accessToken + "<br>");
var client = new FacebookClient(accessToken);
//var client = new FacebookClient();
dynamic me = client.Get("me");
string firstName = me.first_name;
string lastName = me.last_name;
Response.Write(firstName + " " + lastName);
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
email.Text = loginInfo.Email;
}
}
}
Regards,
Moayyad

Asp.net Active Directory LDAP: Trying to filter down into a group level

I have created a login page in asp.net using c# but I am having difficulty trying to only allow a certain group to have access. Right now I am accessing everyone but I can't seem to just filter the group that I need so only those person(s) can have access to my application.
Any help would be great so I can just permission this application out to that one group within Active Directory.
Here is my class that I am using to pass the groups:
public class LdapAuthentication
{
private string _path;
private string _filterAttribute;
public LdapAuthentication(string path)
{
_path = path;
}
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry( _path, domainAndUsername, pwd);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if(null == result)
{
return false;
}
// Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
public string GetGroups()
{
DirectorySearcher search = new DirectorySearcher(_path);
search.Filter = "(cn=" + _filterAttribute + ")";
search.PropertiesToLoad.Add("memberOf");
StringBuilder groupNames = new StringBuilder();
try
{
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
String dn;
int equalsIndex, commaIndex;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
dn = (String)result.Properties["memberOf"][propertyCounter];
equalsIndex = dn.IndexOf("=", 1);
commaIndex = dn.IndexOf(",", 1);
if (-1 == equalsIndex)
{
return null;
}
groupNames.Append(dn.Substring((equalsIndex + 1),
(commaIndex - equalsIndex) - 1));
groupNames.Append("|");
}
}
catch (Exception ex)
{
throw new Exception("Error obtaining group names. " +
ex.Message);
}
return groupNames.ToString();
}
public bool isMember( String groupname )
{
DirectorySearcher search = new DirectorySearcher(_path);
search.Filter = "(cn=" + _filterAttribute + ")";
search.PropertiesToLoad.Add("memberOf");
try
{
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
String dn = (String)result.Properties["memberOf"][propertyCounter];
// The comma in the regex is important to prevent accidental matches
if ( Regex.IsMatch( dn, #"cn="+groupname+",.*", RegexOptions.IgnoreCase))
return true;
}
}
catch (Exception ex)
{
// Some logging here probably
}
return false;
}
}
It has to be in the Get groups function but I am not sure how to pass the group I am looking for. If anyone can help that would be greatly appreciated. Thanks in advance.
Here is my button click event:
protected void btnLogin_Click(object sender, EventArgs e)
{
// Path to you LDAP directory server.
// Contact your network administrator to obtain a valid path.
string adPath = "LDAP://domain.com";
LdapAuthentication adAuth = new LdapAuthentication(adPath);
String myGroupName = "Some_Group";
try
{
if (true == adAuth.IsAuthenticated(txtDomainName.Text, txtLoginID.Text, txtPassword.Text))
{
if( adAuth.isMember( myGroupName ) )
{
// User is authenticated and a member of the group.
// Create your auth ticket, cookie, and whatnot
// Retrieve the user's groups
string groups = adAuth.GetGroups();
// Create the authetication ticket
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, // version
txtLoginID.Text,
DateTime.Now,
DateTime.Now.AddMinutes(60),
false, groups);
// Now encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Create a cookie and add the encrypted ticket to the
// cookie as data.
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
// Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
// Redirect the user to the originally requested page
//Response.Redirect(FormsAuthentication.GetRedirectUrl(txtLoginID.Text, false));
Response.Redirect("LookupEdit.aspx");
}
else
{
lblError.Text = "Authorization failed. You are not a member of the "+myGroupName+" group";
}
}
else
{
lblError.Text = "Authentication did not succeed. Check user name and password.";
}
}
catch(Exception ex)
{
lblError.Text = "Error authenticating. " + ex.Message;
}
}
OK so after your comments, here's what you should do:
Create an isMember(String) function in your LdapAuthentication class:
public boolean isMember( String groupname )
{
DirectorySearcher search = new DirectorySearcher(_path);
search.Filter = "(cn=" + _filterAttribute + ")";
search.PropertiesToLoad.Add("memberOf");
try
{
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
String dn = (String)result.Properties["memberOf"][propertyCounter];
// The comma in the regex is important to prevent accidental matches
if ( Regex.IsMatch( dn, #"cn="+groupname+",.*", RegexOptions.IgnoreCase)
return true;
}
}
catch (Exception ex)
{ // Some logging here probably
}
return false;
}
Next, add this as your btnlogin_Click code:
<script runat=server>
void btnlogin_Click(Object sender, EventArgs e)
{
String adPath = "LDAP://your.domain.com"; //Enter your domain name here
LdapAuthentication adAuth = new LdapAuthentication(adPath);
String myGroupName = "auth-group-name"; //Enter your group's name (cn) here
try
{
if(true == adAuth.IsAuthenticated(txtDomain.Text, txtUsername.Text, txtPassword.Text))
{
if( adAuth.isMember( myGroupName ) )
{
// User is authenticated and a member of the group.
// Create your auth ticket, cookie, and whatnot
}
else
{
errorLabel.Text = "Authorization failed. You are not a member of the "+myGroupName+" group";
}
}
else
{
errorLabel.Text = "Authentication did not succeed. Check user name and password.";
}
}
catch(Exception ex)
{
errorLabel.Text = "Error authenticating. " + ex.Message;
}
}
</script>
This should do the trick for you. Obviously you should create some code after the isMember() check to create a secure cookie that allows other pages to check the authenticated state of the user.
Good luck.
I was also having issues with this. I use pretty much the same class as you do to do the AD authentication. I was using a different way to connect to AD but was having some strange problems. I implemented a Role Provider before I changed to this code and I'm still using that Role provider to deny and grant access. This is how I basically did it. Follow this link as it will help you set up the roles.
The only thing I changed up was the "GetRolesForUser" with..
public override string[] GetRolesForUser(string username)
{
var allRoles = new List();
var ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if (user != null)
{
var groups = user.GetGroups();
allRoles.AddRange(groups.Select(x => x.Name));
}
return allRoles.ToArray();
}
This way, you can deny and grant groups within the web.config.
I do it like this..
<location path="TestPage.aspx">
<system.web>
<authorization>
<allow roles="SoftwareDevelopers" />
<deny users="*" />
</authorization>
</system.web>
</location>
So I'm denying everyone access to the TestPage.aspx besides the AD group SoftwareDevelopers.
I hope this helps.
EDIT: If you are using the link that is in the comment to do this active directory, one way to get the group is using the authentication ticket that's created when you login.
if (Request.Cookies["ADAuthCookie"] != null)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
string cookiePath = ticket.CookiePath;
DateTime expiration = ticket.Expiration;
bool expired = ticket.Expired;
bool isPersistent = ticket.IsPersistent;
DateTime issueDate = ticket.IssueDate;
string name = ticket.Name;
string userData = ticket.UserData;
int version = ticket.Version;
System.Diagnostics.Debug.WriteLine(cookiePath);
System.Diagnostics.Debug.WriteLine(expiration);
System.Diagnostics.Debug.WriteLine(expired);
System.Diagnostics.Debug.WriteLine(isPersistent);
System.Diagnostics.Debug.WriteLine(issueDate);
System.Diagnostics.Debug.WriteLine(name);
System.Diagnostics.Debug.WriteLine(userData);
System.Diagnostics.Debug.WriteLine(version);
if (userData.Contains("SoftwareDevelopers"))
{
lblMessage.Text = "You're a software developer.";
}
}
You will have to decrypt the ticket to be able to read the information. I have provided how to write it to the console so you could see how it works. If you wanted to, the if statement provided above is one way you can restrict/show parts of a page.
However, I'm finding that the link in this answer on how to set up the ADRoleProvider is probably going to be your best route.
You want to check if the user is a member of the correct group, right?
Why not create a function that accepts a group name and returns boolean, like this:
public boolean isMember( String username, String groupname )
{
DirectorySearcher search = new DirectorySearcher(_path);
search.Filter = "(cn=" + username + ")";
search.PropertiesToLoad.Add("memberOf");
try
{
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
String dn = (String)result.Properties["memberOf"][propertyCounter];
// The comma in the regex is important to prevent accidental matches
if ( Regex.IsMatch( dn, #"cn="+groupname+",.*", RegexOptions.IgnoreCase)
return true;
}
}
catch (Exception ex)
{ // Some logging here probably
}
return false;
}
The best way is to let LDAP do the membership iteration, by specifying the groupname in your search. However that requires the groupname to be the complete group DN (e.g. cn=mygroup,ou=groups,dc=xxx,dc=xxx) so this might not be feasible for you:
public boolean isMember( String username, String groupdn )
{
DirectorySearcher search = new DirectorySearcher(_path);
// Escaping the input strings is advisable from a security standpoint!
search.Filter = "(&(cn=" + username + ")(memberOf=" + groupdn + "))";
search.PropertiesToLoad.Add("cn");
try
{
SearchResult result = search.FindOne();
// The LDAP server has done the matching for us
if ( result != null )
return true;
}
catch (Exception ex)
{ // Some logging here probably
}
return false;
}
Good luck!

Windows Phone 8 Basic Authentication

I have hosted a Windows Web API using a basic authentication header with username and password.
I'm trying to create a login form that takes a username and password and sends back a token.
so i have the following code.
I'm using a Attributed method
public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
private IPromiseRepository promiseRepository;
public BasicAuthenticationAttribute()
{
this.promiseRepository = new EFPromiseRepository(new PropellorContext());
//repository = promiseRepository;
}
public BasicAuthenticationAttribute(IPromiseRepository promiseRepository, INewsFeedRepository newsfeedRepository)
{
this.promiseRepository = promiseRepository;
}
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization == null)
{
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
else
{
string authToken = actionContext.Request.Headers.Authorization.Parameter;
string decodedToken = authToken;
// Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
string username = decodedToken.Substring(0, decodedToken.IndexOf(":"));
string password = decodedToken.Substring(decodedToken.IndexOf("^")+1);
string APIToken = decodedToken.Substring(decodedToken.IndexOf("="));
APIToken = APIToken.Replace("=", string.Empty);
password = password.Replace("=", string.Empty);
if (!string.IsNullOrEmpty(APIToken))
{
password = password.Replace(APIToken, string.Empty);
}
if (username != null && password != null)
{
try
{
var user = promiseRepository.GetUserByName(username);
var salt = user.PasswordSalt;
System.Security.Cryptography.SHA512Managed HashTool = new System.Security.Cryptography.SHA512Managed();
Byte[] PasswordAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(password, salt));
Byte[] EncryptedBytes = HashTool.ComputeHash(PasswordAsByte);
HashTool.Clear();
var hashedpass = Convert.ToBase64String(EncryptedBytes);
if (hashedpass == user.Password)
{
if (string.IsNullOrEmpty(user.APIToken))
{
String guid = System.Guid.NewGuid().ToString();
user.APIToken = guid;
promiseRepository.UpdateUser(user);
promiseRepository.Save();
}
if (user != null)
{
user = promiseRepository.GetUserByUserID(user.UserID);
HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(user), new string[] { });
base.OnActionExecuting(actionContext);
}
}
if (APIToken != null)
{
if (user.APIToken == APIToken)
{
var userbytoken = promiseRepository.GetUserByAPIToken(APIToken);
HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(userbytoken), new string[] { });
base.OnActionExecuting(actionContext);
}
}
}
catch (Exception)
{
{
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
base.OnActionExecuting(actionContext);
}
throw;
}
}
}
}
}
This works with Fiddler when the correct credentials are passed
I'm attempting to produce the same authentication in my windows phone application.
Passes a username and password into the basic authentication http header.
However I'm not sure how to do this after a large amount of diggging on the internet alot of the exmaples are windows phone 7 and certain methods don't exist anymore.
This is the code i have arrived at.
private void Login1_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:5650/api/start");
NetworkCredential credentials = new NetworkCredential(userName.Text + ":^",password.Text + "=");
request.Credentials = credentials;
request.BeginGetResponse(new AsyncCallback(GetSomeResponse), request);
Hopefully someone can guide me into the right direction.
it should be simple in principle :(
Here is a sample using HttpClient:
public static async Task<String> Login(string username, string password)
{
HttpClient Client = new HttpClient();
Client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(StringToAscii(string.Format("{0}:{1}", username, password))));
var response = await Client.GetAsync(new Uri(new Uri("http://yourdomain.com"), "/login"));
var status= await response.Content.ReadAsAsync<String>();
return status;
}
And of course you can find the ToBase64String function on the internet. The tricky part here is the Authorization header.

Encrypt and decrypt FormsAuthenticationTicket to authenticate users

I'm trying to create my own authentication mechanism which relies on FormsAuthentication. I'm basically using OAuth to allow users to authenticate in an Authorization Server, and once they are authenticated, I need to use FormsAuthentication to authenticate them across the session. So anyway, I created an HttpModule and a helper class to make this work. Unfortunately, it does not.
What happens is that on PostAuthenticateRequest I encrypt the ticket and add a cookie to the response, then redirect the user to the root of the website. Once the user is re-directed, another HTTP request is issued so the HttpModule is triggered again, and on the AuthenticateRequest event I'm checking whether this user is authenticated or not. In order to check if the user is authenticated, I'm trying to read the cookie, get the username from it and then set the Thread.CurrentPrincipal property. But, for some reason, the cookie cannot be found.
Here's my code:
public class OAuthModule : IHttpModule
{
private const String USERNAME = "username";
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += context_AuthenticateRequest;
context.PostAuthenticateRequest += context_PostAuthenticateRequest;
}
void context_PostAuthenticateRequest(object sender, EventArgs e)
{
var application = sender as HttpApplication;
if (application != null)
{
String username = application.Context.Items[USERNAME].ToString();
String uri = RemoveQueryStringFromUri(application.Context.Request.Url.AbsoluteUri);
var cookie = IdentityHelper.GetEncryptedFormsAuthenticationCookie(username, uri);
application.Context.Response.Cookies.Add(cookie);
application.Context.Response.Redirect(uri);
}
}
void context_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = sender as HttpApplication;
if (sender != null)
{
if (!application.Context.Request.Url.AbsolutePath.Contains("."))
{
if (!IdentityHelper.IsAuthenticated)
{
HttpContextWrapper wrapper = new HttpContextWrapper(application.Context);
String clientId = WebConfigurationManager.AppSettings["ClientId"];
String clientSecret = WebConfigurationManager.AppSettings["ClientSecret"];
String authorizationServerAddress = WebConfigurationManager.AppSettings["AuthorizationServerAddress"];
var client = OAuthClientFactory.CreateWebServerClient(clientId, clientSecret, authorizationServerAddress);
if (String.IsNullOrEmpty(application.Context.Request.QueryString["code"]))
{
InitAuthentication(wrapper, client);
}
else
{
OnAuthCallback(wrapper, client);
}
}
}
}
}
private void InitAuthentication(HttpContextWrapper context, WebServerClient client)
{
var state = new AuthorizationState();
var uri = context.Request.Url.AbsoluteUri;
uri = RemoveQueryStringFromUri(uri);
state.Callback = new Uri(uri);
var address = "https://localhost";
state.Scope.Add(address);
OutgoingWebResponse outgoingWebResponse = client.PrepareRequestUserAuthorization(state);
outgoingWebResponse.Respond(context);
}
private void OnAuthCallback(HttpContextWrapper context, WebServerClient client)
{
try
{
IAuthorizationState authorizationState = client.ProcessUserAuthorization(context.Request);
AccessToken accessToken = AccessTokenSerializer.Deserialize(authorizationState.AccessToken);
String username = accessToken.User;
context.Items[USERNAME] = username;
}
catch (ProtocolException e)
{
EventLog.WriteEntry("OAuth Client", e.InnerException.Message);
}
}
private String RemoveQueryStringFromUri(String uri)
{
int index = uri.IndexOf('?');
if (index > -1)
{
uri = uri.Substring(0, index);
}
return uri;
}
}
public class IdentityHelper
{
public static Boolean IsAuthenticated
{
get
{
String username = DecryptFormsAuthenticationCookie();
if (!String.IsNullOrEmpty(username))
{
SetIdentity(username);
return Thread.CurrentPrincipal.Identity.IsAuthenticated;
}
return false;
}
}
private static String DecryptFormsAuthenticationCookie()
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
return ticket.UserData;
}
return String.Empty;
}
internal static HttpCookie GetEncryptedFormsAuthenticationCookie(String username, String domain)
{
var expires = DateTime.Now.AddMinutes(30);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, expires, true, username, FormsAuthentication.FormsCookiePath);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName);
cookie.Value = FormsAuthentication.Encrypt(ticket);
cookie.Domain = domain;
cookie.Expires = expires;
return cookie;
}
private static void SetIdentity(String username)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.Name, username) });
var principal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = principal;
}
}
Where did I go wrong? Any ideas?
Ok so I finally got it solved. It was as simple as the following:
application.Context.Response.Redirect(uri, false);
I needed to tell the module not to kill the current response (hence the false) so it would preserve the cookie in the coming request.

Resources