Pretty much as the title says, users log in using Basic auth over SSL and call a WebService method, I would like to extract the auth headers and use the username parameter in the method function. Is there an easy way to do this either inline (inside the Method itself), or as a Class method in a separate project?
.NET 4.5, IIS 7.5
Thanks
After some searching around I discovered some code that should do what I want:
string authHeader = WebClient.Headers[HttpRequestHeader.Authorization];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
return usernamePassword.Substring(0, seperatorIndex);
}
But on compile Visual Studio errors with: An object reference is required for the non-static field, method, or property 'System.Net.WebClient.Headers.get'.
I know that once I can populate the authHeader string everything else will work as expected, but I am struggling with getting the actual header values.
Finally found the solution that worked for us, hopefully nobody sees any major problem with it, but if you do, please share it.
private string username
{
get{
HttpContext ctx = HttpContext.Current;
string authHeader = ctx.Request.Headers["Authorization"];
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
return usernamePassword.Substring(0, seperatorIndex);
}
}
And that seems to solve our issue, we didn't want to send the username over separate to the headers, because it would allow someone to alter username parameter without it affecting the account they logon with, this way the account that they logon with is the the account we use to create our links, and it works.
Related
I would like to know, how one can get the response containing auth code from google, when the redirect uri is a custom uri.
This is the code that i use. I open the url in a browser.( currently testing in ios-simulator)
var url = "https://accounts.google.com/o/oauth2/v2/auth?";
string scope = "email%20profile";
string redirect_uri = "com.companyname.exampleauth://";
string response_type = "code";
string access_type = "offline";
Once user allows the app, it goes back to the app! but, how do i get the response with code?
In the info.plist under the Advance tab you'll see URL Types. Register your app there. Use your bundle identifer as the Identifier, your redirect_uri as the URL scheme and editor for role.
The AppDelegate will get called when the user is done. Heres an example:
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
if (url.Scheme == "yourScheme") {
var queryParams = url.Query.Split('&');
return true;
}
return false;
}
I am using the Microsoft.AspNet.Mvc.Facebook NuGet package. I would like to exchange a regular token for the extended access token (the one that replaced the offline_access permission).
From Googling around I found the URL should be in this format:
https://graph.facebook.com/oauth/access_token?
client_id=[APP_ID]&
client_secret=[APP_SECRET]&
grant_type=fb_exchange_token&
fb_exchange_token=[EXISTING_ACCESS_TOKEN]
So I use the following code:
var longToken = await context.Client.PostTaskAsync("/oauth/access_token",
new
{
client_id = fbApp.AppId,
client_secret = fbApp.AppSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = context.AccessToken
});
This returns a null. No error or anything. Just a null value.
Edit: Also tried the following, which also did not work. But a GET seems more logical than a POST anyway.
dynamic result = context.Client.Get("oauth/access_token",
new
{
client_id = fbApp.AppId,
client_secret = fbApp.AppSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = context.AccessToken
});
var longToken = result.access_token as string;
I have successfully done this as a GET request, not a POST :) Just put the necessary parameters into the URL and request it as a GET request and the response returns the long term access token.
EDIT
When you get the result from this, you should parse the query string first (am not sure in C# but maybe you could use this link: http://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx).
After that try to get the access_token property and I got it right on my end. I was doing it in node.js though, but essentially the same flow.
Every Example I can find is either very outdated or always comes back 401.
Can anyone at all provide a working example of posting a status update to twitter?
Even the below always fails.
I get redirected to twitter - great. I can confrim the access codes are correct and match my application, but on acutally posting the update - error is unknown...
What on earth is wrong here? Does matter what app I use or which twitter account.
Using Twitteriser2.dll
if (Request["oauth_token"] == null)
{
OAuthTokenResponse reqToken = OAuthUtility.GetRequestToken(
oauth_consumer_key,
oauth_consumer_secret,
Request.Url.AbsoluteUri);
Response.Redirect(string.Format("http://twitter.com/oauth/authorize?oauth_token={0}",
reqToken.Token));
}
else
{
string requestToken = Request["oauth_token"].ToString();
string pin = Request["oauth_verifier"].ToString();
var tokens = OAuthUtility.GetAccessToken(
oauth_consumer_key,
oauth_consumer_secret,
requestToken,
pin);
OAuthTokens accesstoken = new OAuthTokens()
{
AccessToken = tokens.Token,
AccessTokenSecret = tokens.TokenSecret,
ConsumerKey = oauth_consumer_key,
ConsumerSecret = oauth_consumer_secret
};
TwitterResponse<TwitterStatus> response = TwitterStatus.Update(
accesstoken,
"Testing!! It works (hopefully).");
if (response.Result == RequestResult.Success)
{
Response.Write("we did it!");
}
else
{
Response.Write("it's all bad.");
}
}
The TwitterRepsonse object has an "ErrorMessage" property. You should probably start by looking at the information in there to give you some guidance.
Why don't you use Tweetinvi. Tweetinvi will allow you to post in 1 line and get error messages in line too. Here is an example.
TwitterCredentials.SetCredentials("Access_Token", "Access_Token_Secret", "Consumer_Key", "Consumer_Secret");
var tweet = Tweet.PublishTweet("Hello!");
if (tweet == null)
{
var exceptionDetails = ExceptionHandler.GetLastException().TwitterExceptionInfos.First().Message;
}
You can find the documentation here : https://tweetinvi.codeplex.com/documentation
Also have a look at https://tweetinvi.codeplex.com/discussions/536895 if you are using it with ASP.NET.
Got it working eventually.
The fault isn't exactly known as I didn't have to change the code but what I did was re-download Twitterizwer and Built it (Required adding a ref to C++ components for what ever reason) and it then worked so I can only see that it was somehow faulty the first time round.
I'm trying to create a client for the new tent.io protocol that's being developed and they are using the HTTP MAC Oauth2 scheme described by https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-http-mac-01.
I've written a simple method in C# that creates the Authorization header, but when I submit my request I get a simple "Invalid MAC signature" error.
Since I don't have a reference implementation, I'm struggling to figure out what's wrong with my code. I'm posting it here in the hope that somebody can spot my mistake.
public string GetAuthorizationHeader(string macKeyIdentifier, string macKey, string macAlgorithm, string method, Uri uri)
{
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
string timestamp = ((int)t.TotalSeconds).ToString();
string nonce = new Random().Next().ToString();
string normalizedString = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n\n",
timestamp,
nonce,
method,
uri.PathAndQuery,
uri.Host,
uri.Port);
HashAlgorithm hashGenerator = null;
if (macAlgorithm == "hmac-sha-256")
{
hashGenerator = new HMACSHA256(Encoding.ASCII.GetBytes(macKey));
}
else if (macAlgorithm == "hmac-sha-1")
{
hashGenerator = new HMACSHA1(Encoding.ASCII.GetBytes(macKey));
}
else
{
throw new InvalidOperationException("Unsupported MAC algorithm");
}
string hash = System.Convert.ToBase64String(hashGenerator.ComputeHash(Encoding.ASCII.GetBytes(normalizedString)));
StringBuilder authorizationHeader = new StringBuilder();
authorizationHeader.AppendFormat(#"id=""{0}"",ts=""{1}"",nonce=""{2}"",mac=""{3}""",
macKeyIdentifier, timestamp, nonce, hash);
return authorizationHeader.ToString();
}
I create the full header using the returned value and it looks something lke this
Authorization: MAC id="a:dfsdfa2",ts="1349277638",nonce="1469030797",mac="ibZ/HXaoz2VgBer3CK7K9vu0po3K+E36K+TQ9Sgcw6o="
I'm sure I'm missing something small, but I cannot see it.
Any help would be very much appreciated!
It turns out the code above is perfect, but I was passing the wrong HTTP method value into it!
Where I was getting the error, I was POST'ing JSON, but I had actually put "GET" into the GetAuthorizationMethod!
Once I'd corrected that, I got an access_token value from Tent.is.
Nicely executed tool at http://buchananweb.co.uk/security01.aspx showing HMAC using MD5 and SHA1, SHA256, SHA384, SHA512
I currently use LogonUser() to authenticate my user's username and password on my local domain at the office and it works great for what i need it to do.
Since I developed the app I now need to make it work over my VPN. It seems LogonUser() will not work with REMOTELY validating credentials. Or will it? Is it possible to use LogonUser() to validate a user's credentials on a REMOTE domain account?
I have read in some places that using LOGON32_LOGON_NEW_CREDENTIALS for the 4th param (login type) and LOGON32_PROVIDER_WINNT50 for the 5th param (provider) would do the trick. But every time I try that I ALWAYS get success... I can supply a bogas user and pass and it will work every time :(.
Ideas?
Edit - Added Notes
Tried to use this function but I kept getting the exception telling me the user/pass was bad.
public bool Win2kCredentialsIsValid(string domain, string username, string password)
{
string adPath = "LDAP://" + domain + "/rootDSE";
DirectoryEntry adRoot = new DirectoryEntry(adPath, domain + "\\" + username, password, AuthenticationTypes.ReadonlyServer);
try
{
object o = adRoot.Properties["defaultNamingContext"];
}
catch
{
return false;
}
return true;
}
--
Edit - Added More Notes
OK so I tried yet another example just to get it to work and started down this path, and there are a few things to note...
MyServerHostName is exactly that, my server's hostname. EX: 'Server01'.
My domain name in this example is 'MyDomain.local'
So that makes my FQN for the server 'Server01.MyDomain.local'
I tried to make this work and got the following error...
The supplied context type does not match the server contacted. The server type is Domain.
This errored out at : var context = new PrincipalContext(ContextType.ApplicationDirectory, "MyServerHostName:389", "DC=MyDomain,DC=local"))
private bool CheckADCredentials()
{
bool bResults;
using (var context = new PrincipalContext(ContextType.ApplicationDirectory,
"MyServerHostName:389",
"DC=MyDomain,DC=local"))
{
var username = "firstname.lastname";
var email = "firstname.lastname#MyServerHostName";
var password = "123456";
var user = new UserPrincipal(context)
{
Name = username,
EmailAddress = email
};
user.SetPassword(password);
user.Save();
if (context.ValidateCredentials(username, password, ContextOptions.SimpleBind))
{
bResults = true;
}
else
{
bResults = false;
}
user.Dispose();
}
return bResults;
}
I ended up going with a different solution. Instead of trying to validate a user's account on a domain that my PC was not connected to I ended up caching my domain credentials in the database and just built a salted MD5 type encrypt function so it would make it hard .. er.. for someone to crack it. ;)
Now I just validate against cached credentials in the database when working remotely... It just required the user to first login on the domain but then the user can use it remotely day and night. ;)
Thanks!