I am trying to write some simple tests User Authentication mechanism which uses Basic Authentication. How can I retrieve the credentials from the header?
string authorizationHeader = this.HttpContext.Request.Headers["Authorization"];
Where do I go from here? There are several tutorials but I new to .NET and authentication, could you explain in your answer exactly step-by-step the what and why you are doing.
From my blog:
This will explain in detail how this all works:
Step 1 - Understanding Basic Authentication
Whenever you use Basic Authentication a header is added to HTTP Request and it will look similar to this:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Source: http://en.wikipedia.org/wiki/Basic_access_authentication
"QWxhZGRpbjpvcGVuIHNlc2FtZQ==" is just "username:password" encoded in Base64(http://en.wikipedia.org/wiki/Base64). In order to access headers and other HTTP properties in .NET (C#) you need to have access to the current Http Context:
HttpContext httpContext = HttpContext.Current;
This you can find in System.Web namespace.
Step 2 - Getting the Header
Authorization header isn't the only only one in the HttpContext. In order to access the header, we need to get it from the request.
string authHeader = this.httpContext.Request.Headers["Authorization"];
(Alternatively you may use AuthenticationHeaderValue.TryParse as suggested in pasx’s answer below)
If you debug your code you will see that the content of that header looks similar to this:
Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Step 3 - Checking the header
You've already extracted the header now there are several things you need to do:
Check that the header isn't null
Check that the Authorization/Authentication mechanism is indeed "Basic"
Like so:
if (authHeader != null && authHeader.StartsWith("Basic")) {
//Extract credentials
} else {
//Handle what happens if that isn't the case
throw new Exception("The authorization header is either empty or isn't Basic.");
}
Now you have check that you are have something to extract data from.
Step 4 - Extracting credentials
Removing "Basic " Substring
You can now attempt to get the values for username and password. Firstly you need to get rid of the "Basic " substring. You can do it like so:
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
See the following links for further details:
http://msdn.microsoft.com/en-us/library/system.string.substring(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/t97s7bs3(v=vs.110).aspx
Decoding Base64
Now we need to decode back from Base64 to string:
//the coding should be iso or you could use ASCII and UTF-8 decoder
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
Now username and password will be in this format:
username:password
Splitting Username:Password
In order to get username and password we can simply get the index of the ":"
int seperatorIndex = usernamePassword.IndexOf(':');
username = usernamePassword.Substring(0, seperatorIndex);
password = usernamePassword.Substring(seperatorIndex + 1);
Now you can use these data for testing.
The Final Code
The final code may look like this:
HttpContext httpContext = HttpContext.Current;
string authHeader = this.httpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic")) {
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
} else {
//Handle what happens if that isn't the case
throw new Exception("The authorization header is either empty or isn't Basic.");
}
Just adding to the main answer, the best way to get rid of the "Basic" substring is to use AuthenticationHeaderValue Class:
var header = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentials = header.Parameter;
It will throw a FormatException if the content of the header is not valid, e.g.: the "Basic" part is not present.
Alternatively if you do not want to have exception, use AuthenticationHeaderValue.TryParse
Awesome answer from #DawidO.
If you are just looking to extract the basic auth creds and rely on the .NET magic given you have HttpContext, this will also work:
public static void StartListener() {
using (var hl = new HttpListener()) {
hl.Prefixes.Add("http://+:8008/");
hl.AuthenticationSchemes = AuthenticationSchemes.Basic;
hl.Start();
Console.WriteLine("Listening...");
while (true) {
var hlc = hl.GetContext();
var hlbi = (HttpListenerBasicIdentity)hlc.User.Identity;
Console.WriteLine(hlbi.Name);
Console.WriteLine(hlbi.Password);
//TODO: validater user
//TODO: take action
}
}
}
Remember, using strings can be less secure. They will remain in memory untill they are picked by GC.
Related
I am developing a site where the users will be able to click a "Forgot My Password" button to reset their passwords.
Currently, once the email has been validated, the following code should generate a token to be emailed to the user:
if(validUser != null)
{
var generationTime = DateTime.Now;
var pwToken = await _userManager.GeneratePasswordResetTokenAsync(validUser);
await _userManager.UpdateAsync(validUser);
var url = $"https://{Request.Host}/verify/{HttpUtility.UrlEncode(pwToken)}";
//EmailHelper.SendMagicLinkEmail(validUser, url, Request);
return new RedirectResult("/");
}
All information online regarding this seems to suggest that this is the way to do things. I have set up the Default token providers in the Startup.csfile too:
identityOptions: o => {
o.User.RequireUniqueEmail = true;
o.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultProvider;
o.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultProvider;
},
Yet when a token is generated it produces a large token such as this:
CfDJ8CnvAYtZf+1IjXpKUM7+umDYEaImg2SPFglPX3Y8RmYpEfg5zpK8xL54lvlbJUd54CaIzzYlff/GU+xKKS8mmG5UdC1zdk24nOsJNpIlmC3P5V72BchS4P9DGFTR77XiKbMAAYymnMomS2zCdTKh+E4bn9RI6FVinMecG1HR7nSHmOI2McbXHBFTanI/0uwxH5WI/Dj4AFTBP39ni7mfKkeWz2nJ5pTemELJJ6pYP50+
The problem here is obviously the forward slashes, which cause issues with routing so are encoded out here:
var url = $"https://{Request.Host}/verify/{HttpUtility.UrlEncode(pwToken)}";
The problem is that even with that, .Net Core seems to un-encode it and produce the following error when the generated link is accessed:
This error isn't necessarily the issue, and I do understand it's importance. Yet I can't seem to find any explanation as to why this token is behaving this way. All online examples seem to produce a fairly standard GUID style token, not something such as this.
Does anyone know why this might be happening?
Cheers
You may want to try the Url.Action() method:
Example:
var token = userManager.GeneratePasswordResetTokenAsync(user).Result;
var resetLink = Url.Action("ResetPassword","Account", new { token = token }, protocol: HttpContext.Request.Scheme);
var message = "Click here to reset your password";
//Then send your message to the user
Note in the example above the email must be HTML for the link to work
The token looks fairly normal to me.
I think the URL encoding method you'd want to use is Uri.EscapeDataString. What I've personally done is using a UriBuilder and escaped the query string values (in this case for email confirmation):
var uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "my.website.com",
Path = "/confirmEmail",
Query = $"email={Uri.EscapeDataString(email)}&token={Uri.EscapeDataString(token)}"
};
var fullUrl = uriBuilder.Uri.AbsoluteUri;
For you that'd be:
var uriBuilder = new UriBuilder
{
Scheme = "https",
Host = Request.Host,
Path = $"/verify/{Uri.EscapeDataString(pwToken)}"
};
var fullUrl = uriBuilder.Uri.AbsoluteUri;
Background
I have a back end application that has a Twitter app setup and I can query and pull user tweet/post data. This is great, however, right now on the front end I don't have full Twitter integration setup. What I mean by this is that on the front end the user can enter any Twitter username and I want to know for sure that the Twitter username entered actually belongs to the user. With a Twitter application key you can pull public Twitter data for any twitter account which works well for large scale data ingestion and in my case proof of concept kind of work. At the point I am now, I need to have the assumption enforced in the back end that the data being analyzed for a particular Twitter screen name is also owned by the user of the account on my web application.
The proposed Twitter Solution
Here is a bunch of reference documentation I have been trying to follow.
https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter
https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token
https://oauth.net/core/1.0/#anchor9
https://oauth.net/core/1.0/#auth_step1
I have been trying to follow this and I have had different permutations to the code posted below (one without the callback URL as parameters, one with etc.) but at this point, not very different. I have not had any success and it's been more than a couple of days, which is killing me.
The code
This is my attempt to follow the OAuth specification proposed above in the documentation. Note that this is ASP.NET Core 2.2 + code. Also, this is the code for just Step 1 in the Twitter guide for OAuth authentication and authorization.
public async Task<string> GetUserOAuthRequestToken()
{
int timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(timestamp.ToString()));
string consumerKey = twitterConfiguration.ConsumerKey;
string oAuthCallback = twitterConfiguration.OAuthCallback;
string requestString =
twitterConfiguration.EndpointUrl +
OAuthRequestTokenRoute;
string parameterString =
$"oauth_callback={WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}&" +
$"oauth_consumer_key={twitterConfiguration.ConsumerKey}&" +
$"oauth_nonce={nonce}&" +
$"oauth_signature_method=HMAC_SHA1&" +
$"oauth_timestamp={timestamp}" +
$"oauth_version=1.0";
string signatureBaseString =
"POST&" +
WebUtility.UrlEncode(requestString) +
"&" +
WebUtility.UrlEncode(parameterString);
string signingKey =
twitterConfiguration.ConsumerSecret +
"&" + twitterConfiguration.AccessTokenSecret;
byte[] signatureBaseStringBytes = Encoding.ASCII.GetBytes(signatureBaseString);
byte[] signingKeyBytes = Encoding.ASCII.GetBytes(signingKey);
HMACSHA1 hmacSha1 = new HMACSHA1(signingKeyBytes);
byte[] signature = hmacSha1.ComputeHash(signatureBaseStringBytes);
string authenticationHeaderValue =
$"oauth_nonce=\"{nonce}\", " +
$"oauth_callback=\"{WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}\", " +
$"oauth_signature_method=\"HMAC_SHA1\", " +
$"oauth_timestamp=\"{timestamp}\", " +
$"oauth_consumer_key=\"{twitterConfiguration.ConsumerKey}\", " +
$"oauth_signature=\"{Convert.ToBase64String(signature)}\", " +
$"oauth_version=\"1.0\"";
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(
baseUri: new Uri(twitterConfiguration.EndpointUrl),
relativeUri: OAuthRequestTokenRoute);
request.Content = new FormUrlEncodedContent(
new Dictionary<string, string>() {
{ "oauth_callback", twitterConfiguration.OAuthCallback }
});
request.Headers.Authorization = new AuthenticationHeaderValue("OAuth",
authenticationHeaderValue);
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode)
{
return await httpResponseMessage.Content.ReadAsStringAsync();
}
else
{
return null;
}
}
Notes
I have tried to remove the callback URL from the parameters as well and that didn't work. I have tried all sort of slightly different permutations (urlencoded my signature, added the callback URL in the query string, removed it etc), but I have lost track at this point the one's I have tried and haven't (encodings, quotes etc.).
Ignore the fact that I am not serializing the response into a model yet as the goal is to first hit a success status code!
I have an integration test setup for this method as well and I keep getting 400 Bad Request with no additional information (which makes sense), but is absolutely not helping with debugging.
[Fact]
public async Task TwitterHttpClientTests_GetOAuthRequestToken_GetsToken()
{
var result = await twitterHttpClient.GetUserOAuthRequestToken();
Assert.NotNull(result);
}
As an aside I had some other questions as well:
Is there a way to verify a user's Twitter account without going
through the OAuth flow? The reason I ask this is because getting
through OAuth flow is proving to be difficult
Is it safe to do the first step of the Twitter login workflow on the back end and return the response to the front end? The response
would carry a sensitive token and token secret. (If I were to answer
this myself I would say you have to do it this way otherwise you
would have to hard code app secrets into front end configuration
which is worse). I ask this because this has been on my conscious
since I have started this and I'm worried a bit.
Is there an OAuth helper library for C# ASP.NET Core that can make this easier?
I solved this by writing unit tests and working through the Twitter documentation on Creating A Signature. Since that example provides keys and results, it's possible to verify that your code is correct.
Since you asked about libraries - I wrote LINQ to Twitter with the hope of helping others like myself with this difficult task.
In addition to to signature, the page navigation can be challenging as your code works through the OAuth flow. Please review the Twitter documentation on Obtaining user access tokens to understand this better. I've also documented this in the LINQ to Twitter Wiki on Securing your Applications. Here's how this will work with LINQ to Twitter:
First, I have an OAuthController with a Begin action to redirect a user to for kicking off the authentication process:
public async Task<ActionResult> Begin()
{
//var auth = new MvcSignInAuthorizer
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore(HttpContext.Session)
{
ConsumerKey = configuration["Twitter:ConsumerKey"],
ConsumerSecret = configuration["Twitter:ConsumerSecret"]
}
};
string twitterCallbackUrl = Request.GetDisplayUrl().Replace("Begin", "Complete");
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
}
Notice that it's using an MvcSignInAuthorizer, passing in credentials via the CredentialStore property. If you were using your own raw code, you would be setting up the HTTP request with the Authorization header.
Next, notice that I'm modifying the current URL so that it will reference the same controller, but with the Complete endpoint. That is the oauth_callback that gets sent to Twitter authorization.
That process redirects the user to the Twitter web site, they authorize your app, and then it uses the oauth_callback to redirect the user back to your site. Here's how you handle that:
public async Task<ActionResult> Complete()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore(HttpContext.Session)
};
await auth.CompleteAuthorizeAsync(new Uri(Request.GetDisplayUrl()));
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database,
// isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent
// queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let
// you make queries without re-authorizing.
//
//var credentials = auth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
return RedirectToAction("Index", "Home");
}
Again, you can see that I'm using MvcAuthorizer and completing the request. After completing the request, you'll be able to pull out the oauth_token and oauth_token_secret, as well as screen_name and user_id. You can save these artifacts and re-use them for all subsequent activity by this user, making their experience better because they don't have to log in every time you need to make a request.
On your question about verification, there is a Verify Credentials endpoint.
LINQ to Twitter has an ASP.NET Core Sample, API Samples with 100% API coverate, and full documentation on the Wiki if you want to learn more.
After hours and hours of going through the documentation I found the answer out. Turns out I missed some small details from the guides.
When making a request to oauth/request_token, when you sign the
request, you don't use the access token secret (for this specific request). Also, see the "Getting Signing Key" section of the signing a request guide and read the last few paragraphs. Therefore the signing key
does not have the access token secret
You must UrlEncode every single key and value. You must UrlEncode the authorization header as well.
I will post the updated code for you all here in case you need this in C#. Note that this code is not clean. You should separate OAuth functionality into some other class. This was my attempt to just get it to work.
public async Task<string> GetUserOAuthRequestToken()
{
int timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(timestamp.ToString()));
string consumerKey = twitterConfiguration.ConsumerKey;
string oAuthCallback = twitterConfiguration.OAuthCallback;
string requestString =
twitterConfiguration.EndpointUrl +
OAuthRequestTokenRoute;
string parameterString =
$"oauth_callback={WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}&" +
$"oauth_consumer_key={WebUtility.UrlEncode(twitterConfiguration.ConsumerKey)}&" +
$"oauth_nonce={WebUtility.UrlEncode(nonce)}&" +
$"oauth_signature_method={WebUtility.UrlEncode(OAuthSigningAlgorithm)}&" +
$"oauth_timestamp={WebUtility.UrlEncode(timestamp.ToString())}&" +
$"oauth_version={WebUtility.UrlEncode("1.0")}";
string signatureBaseString =
"POST&" +
WebUtility.UrlEncode(requestString) +
"&" +
WebUtility.UrlEncode(parameterString);
string signingKey =
WebUtility.UrlEncode(twitterConfiguration.ConsumerSecret) +
"&";
byte[] signatureBaseStringBytes = Encoding.ASCII.GetBytes(signatureBaseString);
byte[] signingKeyBytes = Encoding.ASCII.GetBytes(signingKey);
HMACSHA1 hmacSha1 = new HMACSHA1(signingKeyBytes);
byte[] signature = hmacSha1.ComputeHash(signatureBaseStringBytes);
string base64Signature = Convert.ToBase64String(signature);
string authenticationHeaderValue =
$"oauth_nonce=\"{WebUtility.UrlEncode(nonce)}\", " +
$"oauth_callback=\"{WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}\", " +
$"oauth_signature_method=\"{WebUtility.UrlEncode(OAuthSigningAlgorithm)}\", " +
$"oauth_timestamp=\"{WebUtility.UrlEncode(timestamp.ToString())}\", " +
$"oauth_consumer_key=\"{WebUtility.UrlEncode(twitterConfiguration.ConsumerKey)}\", " +
$"oauth_signature=\"{WebUtility.UrlEncode(base64Signature)}\", " +
$"oauth_version=\"{WebUtility.UrlEncode("1.0")}\"";
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(
baseUri: new Uri(twitterConfiguration.EndpointUrl),
relativeUri: OAuthRequestTokenRoute);
request.Headers.Authorization = new AuthenticationHeaderValue("OAuth",
authenticationHeaderValue);
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode)
{
string response = await httpResponseMessage.Content.ReadAsStringAsync();
return response;
}
else
{
return null;
}
}
I have got the access token as a response from the server in Oauth2.0, but i am not able to fetch it into my code. Could anybody help me giving the code in C# or help just giving some part of it?
var uri = new Uri("http://176.66.102.44:8088/#access_token=eHwAIJd%40bo657tdv8XSaS6hYU%40Ov/XiOWBlMdwo4/h63Q1E%40RcJukIbBnDU%40dh/AYJnEQc4/WmTrUCN3snAntvmchEpZ244LiSIexX/Xiw5jxhR0MkNcS0Y/4bx7Qq4Ws3Wmy3bhFbPSmZQh1KRINwOzY32d8Xz72LGwm8VwyFCn9U1Yo8ERan21EKDvcQpdR%40b4/bDm5eRwIbga0hm7Wog7LnDsQ/OggJqP9CMMkZbqMRN881H4sw0ebMfSZyZf448/HIX5Smfr1%409ofCPzQrZ5GpFVCsaftczTiN%40TnVRPsArbUeLDM2eIAXZyd/QbnBTU840UU3KJ0ohvlBvMi7mYgj0u76JTqRqvyjVnqdNQX6yEPu/6F6SUxEaGkcTRp4h9sVTRZ%40aEMMoQ/0p73GahSPB/G6wuJOfT5PBopnSBrspJq5CvCH2dnz5STkc4ZhpUjj2SLxBDxiJLVruNSbri3sY~&token_type=Bearer&expires_in=120");
var fragments = uri.Fragment.Split('&');
string accessToken = null;
if (fragments.Length > 0 && fragments[0].StartsWith("#access_token="))
{
accessToken = fragments[0].Remove(0, "#access_token=".Length);
}
if (accessToken != null)
{
// Do something with the token
}
How this works is we are going to use the Uri class to parse the Url for us.
The Uri class property Fragment will retrieve everything after (and including) the #, as described in the docs.
We will split on the ampersand because we only need the access_token parameter. We don't need token_type or expires_in. We'll ignore those.
The last part is just throwing the excess data away on the string which is the part that says "#access_token=" so we call Remove on the string to do that. This returns a new string to accessToken and then you can do whatever you want with the data.
Also, keep in mind this only works if the access_token is the first part of the fragment. If it's isn't then after you do the Split you'll need to see which index of the array starts with "access_token=" instead
Uri Class
Uri.Fragment Property Info
I'm currently creating a web application on which the user can login via his Google account. This works client side but I would also like to secure REST API calls. To do so, I send the "Google id token" with each request via the "Authorization" header. Now, I would like to verify in C# that the token passed is valid. I found that there is a .NET library to do so but I didn't find anywhere any clear documentation on how to simply validate the token.
Does anyone have some pointer for this?
My answer is the same as the answer above with a little bit more details.
using Google.Apis.Auth;
using Google.Apis.Auth.OAuth2;
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
...
The payload object contains all the information that you need.
According to the "Verify the integrity of the ID token" documentation multiple things must be checked, for the id token to be valid, not just the signature.
One of those is whether "the ID token is equal to [...] your app's client IDs". Since we never give the client ID to GoogleJsonWebSignature.ValidateAsync(token) it seems we need to check it manually. I'm assuming it's really just checking the signature and we need to do all of the other checks manually.
My first shot at this:
bool valid = true;
try
{
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
if (!payload.Audience.Equals("YOUR_CLIENT_ID_1234567890.apps.googleusercontent.com"))
valid = false;
if (!payload.Issuer.Equals("accounts.google.com") && !payload.Issuer.Equals("https://accounts.google.com"))
valid = false;
if (payload.ExpirationTimeSeconds == null)
valid = false;
else
{
DateTime now = DateTime.Now.ToUniversalTime();
DateTime expiration = DateTimeOffset.FromUnixTimeSeconds((long)payload.ExpirationTimeSeconds).DateTime;
if (now > expiration)
{
valid = false;
}
}
}
catch (InvalidJwtException e)
{
valid = false;
}
For future reference the following verifications are checked internally by the Google.Apis.Auth library and no extra validations are required (both passing settings or checking the payload):
bad jwt (null, empty, too long, missing signature)
wrong algorithm
invalid signature
invalid issuer
signature time without tolerance
The following however require input by the developer in order to be validated. They can be passed with GoogleJsonWebSignature.ValidationSettings:
audience
hosted domain
signature time with tolerance
Source: Google.Apis.Auth.Tests/GoogleJsonWebSignatureTests.cs
According to the docs, the token must be validated by verifying the signature with Google's public key. Also check the aus, iss and exp claims, and the hd claim if applies.
Therefore only the aus (and hd) have to be tested explicitly by the developer.
try
{
//...
var validationSettings = new GoogleJsonWebSignature.ValidationSettings
{
Audience = new string[] { "[google-signin-client_id].apps.googleusercontent.com" }
};
var payload = await GoogleJsonWebSignature.ValidateAsync(idToken, validationSettings);
//...
}
catch (InvalidJwtException ex)
{
//...
}
Yet another simplified answer (for .net 6):
Add this nuget package to your project:
https://www.nuget.org/packages/Google.Apis.Auth
Add using statement:
using Google.Apis.Auth;
Create this method in your controller:
[AllowAnonymous]
[HttpPost("verify")]
public async Task<ActionResult> Verify(){
string token = Request.Headers["Authorization"].ToString().Remove(0,7); //remove Bearer
var payload = await VerifyGoogleTokenId(token);
if (payload==null)
{
return BadRequest("Invalid token");
}
return Ok(payload); }
Create the VerifyGoogleTokenId function:
public async Task<GoogleJsonWebSignature.Payload> VerifyGoogleTokenId(string token){
try
{
// uncomment these lines if you want to add settings:
// var validationSettings = new GoogleJsonWebSignature.ValidationSettings
// {
// Audience = new string[] { "yourServerClientIdFromGoogleConsole.apps.googleusercontent.com" }
// };
// Add your settings and then get the payload
// GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token, validationSettings);
// Or Get the payload without settings.
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token);
return payload;
}
catch (System.Exception)
{
Console.WriteLine("invalid google token");
}
return null;
}
Test the implementation by sending a post request to yourapi.com/verify. Dont forget the authorization header.
Say thanks with an up vote.
I can write and read cookies but I can't change value for existing cookie it always has first set value. I found few ways how it can be implemented but no one works. Here is my code:
private void AddPost(string key)
{
var context = System.Web.HttpContext.Current;
var request = context.Request;
var response = context.Response;
var cookie = request.Cookies[Constants.PostsViewing];
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
response.Cookies.Add(new HttpCookie(Constants.PostsViewing, key)
{
Expires = DateTime.Now.AddDays(365)
});
}
else
{
if (cookie.Value.Split(';').Contains(key))
{
return;
}
var v = cookie.Value + ";" + key;
cookie.Value = v;
cookie.Expires = DateTime.Now.AddDays(365);
response.Cookies.Add(cookie);
// this way also doesn't work
//cookie.Value = v;
//response.AppendCookie(cookie);
// and this
//response.Cookies[Constants.PostsViewing].Value = v;
//response.Cookies[Constants.PostsViewing].Expires = DateTime.Now.AddDays(365);
}
}
According to msdn cookie file should be owerwritten.
Each cookie must have a unique name so that it can be identified later when reading it from the browser. Because cookies are stored by name, naming two cookies the same will cause one to be overwritten.
Do you have any idea how to fix it?
I just ran into this exact scenario with a similar block of code:
public ActionResult Index(int requestValue)
{
var name = "testCookie";
var oldVal = Request.Cookies[name] != null ? Request.Cookies[name].Value : null;
var val = (!String.IsNullOrWhiteSpace(oldVal) ? oldVal + ";" : null) + requestValue.ToString();
var cookie = new HttpCookie(name, val)
{
HttpOnly = false,
Secure = false,
Expires = DateTime.Now.AddHours(1)
};
HttpContext.Response.Cookies.Set(cookie);
return Content("Cookie set.");
}
The first time that code would run, the cookie would be set without incident. But any subsequent run would never update it at all (value or expiration).
Turns out, the semi-colon is an illegal character in a cookie value, and trying to delimit your values with it will cause the cookie value to be truncated. If we change the semi-colon to another character, like a pipe (|), everything works out just fine.
Consider the header sent for a cookie value (courtesy of Fiddler):
Response sent 61 bytes of Cookie data:
Set-Cookie: testCookie=2;1; expires=Tue, 09-Sep-2014 19:23:43 GMT; path=/
As we can see, the semi-colon is being used to separate the individual parts of the cookie definition. Thus, if you want to use a semi-colon in cookie value itself, it must be encoded so as not to be misinterpreted. This answer gives a more detailed look into the actual specification: https://stackoverflow.com/a/1969339/143327.
You can't use a semi-colon, in plain text, as your delimiter.
According to the ancient Netscape cookie_spec:
This string is a sequence of characters excluding semi-colon, comma and white space.
You can't directly modify a cookie. Instead you are creating a new cookie to overrite the old one.
http://msdn.microsoft.com/en-us/library/vstudio/ms178194(v=vs.100).aspx
Try
var v = cookie.Value + ";" + key;
Response.Cookies[Constants.PostsViewing].Value = v;
Response.Cookies[Constants.PostsViewing].Expires = DateTime.Now.AddDays(365);
This should change the client Response instead of the servers Request.
In order to use Response.AppendCookie, you first have to get a HttpCookie from your Cookies collection.