I have a problem with accessing the (payload of) JWT in a dotnet core controller. I don't know where I am wrong. I think I covered all the important points in the following description. If I have missed something, that could help, please let me know.
Thank you for your time.
Adding authentication service to the servicecollection
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = false,
ValidIssuer = null,
ValidAudience = null,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("testify"))
};
});
The token I used for the request:
The postman call:
The code of the controller action:
[HttpPost]
[ProducesResponseType(typeof(void), 201)]
[ProducesResponseType(typeof(void), 400)]
[ProducesResponseType(typeof(void), 401)]
[ProducesResponseType(typeof(void), 403)]
[ApiExplorerSettings(GroupName = "AuditLog")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IActionResult Insert([Required] Dto.Log auditLog) => RunSafely(() =>
{
var log = _mapper.Map<Dto.Log, Log>(auditLog);
log.CorrelationId = _headerReader.GetCorrelationId(Request?.Headers);
_logRepository.AddLog(log);
return this.StatusCode((int)HttpStatusCode.Created);
});
The state of the controller:
Your question is a little unclear to me, too, and I am not sure about the creation process of your token. However, I have the exact scenario in one of my projects, which works fine. Here is the code for different puzzles:
Creating the Token:
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
if (result.Succeeded)
{
// Create the Token
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
_config["Tokens:Issuer"],
_config["Tokens:Audience"],
claims,
expires: DateTime.UtcNow.AddMinutes(120),
signingCredentials: creds);
var results = new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
};
return Created("", results);
}
}
For IServiceCollection :
services.AddAuthentication()
.AddCookie()
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = _config["Tokens:Issuer"],
ValidAudience = _config["Tokens:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]))
};
});
And config.json file: (Note that I changed real-world info due to security reasons)
"Tokens": {
"Key": "foo-foo-foo-foo-foo-foo-foo-foo",
"Issuer": "localhost",
"Audience": "localhost"
}
These settings are exact that I'm using in one of my small projects, and It's working fine. Check if something is missing in your project or not.
You can access payload of jwt by using User object inside of System.Security.Claims.ClaimsPrincipal namespace. For example:
var claims = User.Claims;
This claims contains payload of jwt. Also, you can access other informations like:
if (User.Identity.IsAuthenticated)
{ ....
Within a controller, you can take a dependency on IHttpContextAccessor and then, on the HttpContext, call the extension method GetTokenAsync(string), which will return the encoded string.
string encodedToken = _httpContextAccessor.HttpContext.GetTokenAsync("access_token");
JwtSecurityToken decodedToken = new JwtSecurityToken(encodedToken);
string email = decodedToken.Payload["email"].ToString();
JwtSecurityToken is in the System.IdentityModel.Tokens.Jwt namespace, and GetTokenAsync extension method of HttpContext is in the Microsoft.AspNetCore.Authentication namespace.
RAW PAYLOAD
You can access the raw JWT access token to get its JWTPayload using the following type of code in a .NET API controller:
using System.IdentityModel.Tokens.Jwt;
[Route("api/contacts")]
public class ContactsController : Controller
{
[HttpGet("")]
public async Task<IEnumerable<Contact>> GetContactsAsync()
{
var authHeader = this.HttpContext.Request.Headers.Authorization.ToString();
var parts = authHeader.Split(' ');
var accessToken = parts[1];
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(accessToken);
var payload = token.Payload;
System.Console.WriteLine($"Access token expiry time is {payload.Exp}");
// Business code goes here
}
}
CLAIMS
Microsoft provide a higher level programming model, where by default you never get access to the JWT details. This is a good model to follow - business logic should only ever use a ClaimsPrincipal:
var expiry = this.User.Claims.First(c => c.Type == "exp");
System.Console.WriteLine($"Access token expiry time is {expiry.Value}");
CUSTOM JWT PROCESSING
If you want to implement custom behaviour related to JWTs then it is standard to use the extensibility features of the framework. The plumbing then goes into a middleware class, so that controller classes stay business focused. A couple of example classes of mine show how to extend the .NET framework:
CustomAuthenticationHandler
CustomJWTSecurityTokenHandler
For more info, see these resources of mine, which are focused on promoting understanding of the important standards based OAuth concepts in APIs:
.NET 6 API Code Sample
Blog Post
Easiest way to access payload is by using IHttpContextAccessor injected into your class like below.
public class MyClass: IEwocUser
{
private readonly IHeaderDictionary _headers;
public MyClass(IHttpContextAccessor httpContextAccessor)
{
_headers = httpContextAccessor.HttpContext?.Request.Headers;
}
}
From the headers you can filter for Auth token.
Related
I'm currently developing an identity server. It is multi-tenant with multiple user repositories.
I am able to pass (using Services.OpenIDConnect.Options) my tenant details from my MVC to the IDS in order to select the appropriate user repository on login
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("Tenant", "TenantDetail");
return Task.CompletedTask;
};
I am attempting to obtain this same information for logout, however the initial call to logout has some back end process that calls CustomProfileService.IsActiveAsync(IsActiveContext context).
I am unable to obtain the tenant information from the IsActiveContext, nor am i able to read any kind of query string (as i was using for login).
Any suggestions, or even alternative methods that might be more correct than what I'm attempting, would be greatly appreciated.
OnRedirectToIdentityProvider will not be hit on signout. You'll need to pass the tenant information in the OnRedirectToIdentityProviderForSignOut event in your client instead.
Here's a snippet, that's far from complete:
services
.AddOpenIdConnect("oidc", options =>
{
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProviderForSignOut = context =>
{
context.ProtocolMessage.AcrValues = "tenant:TenantDetail";
return Task.CompletedTask;
},
}
}
In IdentityServer you'll need to lookup the acr_values in the query parameters from the request. Inject IHttpContextAccessor in order to access the context:
public class ProfileService : IProfileService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ProfileService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// ...
}
public async Task IsActiveAsync(IsActiveContext context)
{
// Please note that this method is called on many occasions. Check context.Caller
// This means that you'll have to make sure that the acr_valus are present on all
// ocassions, hence the question in my comment.
var request = _httpContextAccessor.HttpContext.Request;
if (request.Method == HttpMethods.Get)
{
// acr_values should be present on all ocassions.
var values = (string)request.Query["acr_values"];
// This is just a sample, you'll need to parse the values.
var tenant = values.Split(':')[1];
}
// Your code where you link the repository ...
var sub = context.Subject.GetSubjectId();
var user = await userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
Please let me know if this solves the issue for you.
I would like to know how to properly handle the fact that the cookie expired? Is it possible to execute a custom action ?
What I would like to achieve is that when the cookie is expired is to take few informations out of the current cookie at redirect to a action parametrise by this information. Is it possible ?
There isn't a good way to accomplish this. If the cookie is expired, it is not sent to the server to extract any information. With ASP.Net Core Identity, you don't have much control over that. That leaves you to using Cookie Middleware.
This provides a user to a normal redirect when the cookie is expired:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookieAuthenticationOptions>(options =>
{
options.LoginPath = new PathString("/Home/Index");
});
}
The best way to achieve what you're looking for is to set the cookie expiration much later than the true user session expiration, and then perform your session expiration server side and redirect the user at that point. While it's not ideal, you don't have other options when a cookie is expired.
public void ConfigureServices(IServiceCollection services)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
// Redirect when cookie expired or not present
LoginPath = new PathString("/Account/Unauthorized/"),
AutomaticAuthenticate = true,
// never expire cookie
ExpireTimeSpan = TimeSpan.MaxValue,
Events = new CookieAuthenticationEvents()
{
// in custom function set the session expiration
// via the DB and reset it everytime this is called
// if the session is still active
// otherwise, you can redirect if it's invalid
OnValidatePrincipal = <custom function here>
}
});
}
It looks like you need your own handler for the OnValidatePrincipal event when setting up cookie authentication middleware:
OnValidatePrincipal event can be used to intercept and override validation of the cookie identity
app.UseCookieAuthentication(options =>
{
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = <your event handler>
};
});
The ASP.NET documentation contains an example of such a handler:
public static class LastChangedValidator
{
public static async Task ValidateAsync(CookieValidatePrincipalContext context)
{
// Pull database from registered DI services.
var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var userPrincipal = context.Principal;
// Look for the last changed claim.
string lastChanged;
lastChanged = (from c in userPrincipal.Claims
where c.Type == "LastUpdated"
select c.Value).FirstOrDefault();
if (string.IsNullOrEmpty(lastChanged) ||
!userRepository.ValidateLastChanged(userPrincipal, lastChanged))
{
context.RejectPrincipal();
await context.HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");
}
}
}
It seems there is no event for your case but you can use OnRedirectToLogin to change redirect uri. Here is an example:
OnRedirectToLogin = async (context) =>
{
var binding = context.HttpContext.Features.Get<ITlsTokenBindingFeature>()?.GetProvidedTokenBindingId();
var tlsTokenBinding = binding == null ? null : Convert.ToBase64String(binding);
var cookie = context.Options.CookieManager.GetRequestCookie(context.HttpContext, context.Options.CookieName);
if (cookie != null)
{
var ticket = context.Options.TicketDataFormat.Unprotect(cookie, tlsTokenBinding);
var expiresUtc = ticket.Properties.ExpiresUtc;
var currentUtc = context.Options.SystemClock.UtcNow;
if (expiresUtc != null && expiresUtc.Value < currentUtc)
{
context.RedirectUri += "&p1=yourparameter";
}
}
context.HttpContext.Response.Redirect(context.RedirectUri);
}
It should be possible to limit Google API OAuth2 requests to a specific google domain. It used to be possible by hacking on the end &hd=mydomain.com to the request. Using the new MVC auth stuff it seems no longer possible. Any ideas how?
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new AppGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "***.apps.googleusercontent.com",
ClientSecret = "******"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore(HttpContext.Current.Server.MapPath("~/App_Data"), true) ,
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authorizeUri = new Uri(AuthorizationServerUrl).AddQuery("hd", "ourgoogledomain.com"); //is not in the request
var authUrl = new GoogleAuthorizationCodeRequestUrl(authorizeUri)
{
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
//AccessType = "offline",
// ApprovalPrompt = "force"
};
return authUrl;
}
}
Passing a hd parameter is indeed the correct way to limit users to your domain. However, it is important that you verify that the user does actually belong to that hosted domain. I see in your answer that you figured out how to add this parameter back in to your request, so I will address the second part of this.
The issue is that the user can actually modify the requested URL in their client and remove the hd parameter! So while it's good to pass this parameter for the best UI for your users, you need to also verify that authenticated users do actually belong to that domain.
To see which hosted Google Apps for Work domain (if any) the user belongs to, you must include email in the list of scopes that you authorize. Then, do one of the following:
Option 1. Verify the ID Token.
When you exchange your code for an access token, the token endpoint will also return an ID Token in the id_token param (assuming you include an identity scope in your request such as email). If the user is part of a hosted domain, a hd claim will be present, you should check that it is present, and matches what you expect.
You can read more about ID tokens on Google's OpenID Connect docs (including some links to sample code and libraries to help you decode them). This tool can decode ID Tokens during testing.
Option 2. Call UserInfo
Once you have the OAuth Access Token, perform a GET request to https://www.googleapis.com/plus/v1/people/me/openIdConnect with the Access Token in the header. It will return a JSON dictionary of claims about the user. If the user is part of a hosted domain, a hd claim will be present, you should check that it is present, and matches what you expect.
Read more in the documentation for Google's UserInfo endpoint.
The main difference between Option 1 and Option 2 is that with the ID Token, you avoid another HTTP round-trip to the server making it faster, and less error-prone. You can try out both these options interactively using the OAuth2 Playground.
With the updated for .NET core package previous answers are no longer suitable. Fortunately in the new implementation there is a way to hook into authentication events to perform such task.
You will need a class that will handle 2 events - the one that fired before you go to Google and the one for when coming back. In first you limit which domain can be used to sign-in and in the second you ensure that the email with the right domain was in fact used for signin:
internal class GoogleAuthEvents : OAuthEvents
{
private string _domainName;
public GoogleAuthEvents(string domainName)
{
this._domainName = domainName?.ToLower() ?? throw new ArgumentNullException(nameof(domainName));
}
public override Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context)
{
return base.RedirectToAuthorizationEndpoint(new OAuthRedirectToAuthorizationContext(
context.HttpContext,
context.Options,
context.Properties,
$"{context.RedirectUri}&hd={_domainName}"));
}
public override Task TicketReceived(TicketReceivedContext context)
{
var emailClaim = context.Ticket.Principal.Claims.FirstOrDefault(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
if (emailClaim == null || !emailClaim.Value.ToLower().EndsWith(_domainName))
{
context.Response.StatusCode = 403; // or redirect somewhere
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
and then you need to pass this "events handler" to the middleware via GoogleOptions class:
app.UseGoogleAuthentication(new GoogleOptions
{
Events = new GoogleAuthEvents(Configuration["Authentication:Google:LimitToDomain"])
})
#AMH, to do in simplest way you should create your own Google Provider, override method ApplyRedirect and append additional parameter like hd to address which will be using to redirect to a new google auth page:
public class GoogleAuthProvider : GoogleOAuth2AuthenticationProvider
{
public override void ApplyRedirect(GoogleOAuth2ApplyRedirectContext context)
{
var newRedirectUri = context.RedirectUri;
newRedirectUri += string.Format("&hd={0}", "your_domain.com");
context.Response.Redirect(newRedirectUri);
}
}
After that just link new provider to your options:
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "your id",
ClientSecret = "your secret",
Provider = new GoogleAuthProvider(),
});
Having downloaded the source, I was able to see it is easy to subclass the request object, and add custom parameters:
public class GoogleDomainAuthorizationCodeRequestUrl : GoogleAuthorizationCodeRequestUrl
{
/// <summary>
/// Gets or sets the hosted domain.
/// When you want to limit authorizing users from a specific domain
/// </summary>
[Google.Apis.Util.RequestParameterAttribute("hd", Google.Apis.Util.RequestParameterType.Query)]
public string Hd { get; set; }
public GoogleDomainAuthorizationCodeRequestUrl(Uri authorizationServerUrl) : base(authorizationServerUrl)
{
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authUrl = new GoogleDomainAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
Hd = "mydomain.com",
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri
};
return authUrl;
}
}
I found this post when searching for a solution to specify the hosted domain with OpenID Connect integration to Google. I was able to get it working using the Google.Apis.Auth.AspNetCore package and the following code.
In Startup.cs
services.AddGoogleOpenIdConnect(options =>
{
options.ClientId = "*****";
options.ClientSecret = "*****";
options.SaveTokens = true;
options.EventsType = typeof(GoogleAuthenticationEvents);
});
services.AddTransient(provider => new GoogleAuthenticationEvents("example.com"));
Don't forget app.UseAuthentication(); in the Configure() method of Startup.cs.
Then the authentication events class
public class GoogleAuthenticationEvents : OpenIdConnectEvents
{
private readonly string _hostedDomain;
public GoogleAuthenticationEvents(string hostedDomain)
{
_hostedDomain = hostedDomain;
}
public override Task RedirectToIdentityProvider(RedirectContext context)
{
context.ProtocolMessage.Parameters.Add("hd", _hostedDomain);
return base.RedirectToIdentityProvider(context);
}
public override Task TicketReceived(TicketReceivedContext context)
{
var email = context.Principal.FindFirstValue(ClaimTypes.Email);
if (email == null || !email.ToLower().EndsWith(_hostedDomain))
{
context.Response.StatusCode = 403;
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
[This question relates to ASP.NET MVC4, and it is about best-practice approach - so please, don't suggest hacks.]
I want to authenticate users using an auth token sent in the request URL. It works similarly to a password reset token, except in this case it does not go to a reset page but instead grants access to some portion of the site. The idea is to send the URL with the auth token to a verified email address of the user. Users can click the link and perform some actions without typing their password.
Out-of-the-box, ASP.NET has the [Authorize] attribute and the SimpleMembershipProvider - these seem to work great, but they do some voodoo magic under the hood (like auto-generating database tables), so I don't know how to extend them to add this link-based auth token.
I don't expect an exact answer, but please do point me to the right direction.
Thanks!
Uf, broad question. But I will try at least to direct you to a right direction.
So first if suggest that you use Forms Authentication as a base, but you will have to customize using of it. And I presume that you do not want to use cookies for the authentication as this is native behaviour of the Forms Authentication.
The most important point you should consider to have it you custom query string token based authentication.
Create a login action and in this action you will authorize the user, if he have granted access you ask FormsAuthentication to create AuthCookie. For the further on you just take the httpCookie.Value as your auth token that you will carry in query string.
You need to implement the Application_BeginRequest in the Global.asax that will handle this query string tokens and translate it into the cookie. With this approach you can leverage all the ASP.NET Forms Authentication infrastructure.
This is quite high level picture w/o code. If you need more detail help I can also provide it to you.
You should just use a regular Action that accepts HttpGet.
Upon receiving the token, immediately invalid it so it can't be used again.
Also, only accept tokens that are within your pre-defined range of time period, like 24 or 72 hours.
Thank you Peter for idea.
If smb need to create JWT token authorization for old ASP.NET MVC5.I wrote small example. I don't serialize cookie to JWT. I create a JWT and after I am checking it in the BeginRequest. If everything is ok, I create a cookie and set it to the httpContext.Request. I used authentication mode="Forms" for application and it require cookies.
For create JWT token:
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
[AllowAnonymous]
[HttpPost]
public ActionResult LoginJWT(LoginViewModel model)
{
ActionResult response = null;
if (ModelState.IsValid)
{
if (true) //todo: check user login&password
{
var payload = new Dictionary<string, object>
{
{ "iss", "subject" },
{ "sub", "api" },
{ "exp", DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "jti", Guid.NewGuid() },
{ "uid", "64" } //custom field for identificate user
};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
response = Content(token);
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Login or password are not found");
}
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Errors in Model");
}
return response;
}
For check JWT token in Global.asax:
public override void Init()
{
this.BeginRequest += this.BeginRequestHandler;
base.Init();
}
private void BeginRequestHandler(object sender, EventArgs e)
{
var bearerToken = this.Context.Request.Headers["Authorization"];
if (bearerToken != null)
{
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
int userId = 0;
try
{
IJsonSerializer serializer = new JsonNetSerializer();
var provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.DecodeToObject<IDictionary<string, string>>(token, secret, verify: true);
if (json.TryGetValue("uid", out var uid))
{
userId = Convert.ToInt32(uid);
}
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
if (userId != 0)
{
// check user by id, if found create cookie.
}
}
}
I used:
jwt-dotnet/jwt library 7.2.1
I saw couple of libraries which we can use to post to twitter. But I want to create my own as later we need to extend this for other social networks also .
I am using the RESTSharp to make things little easy for me.
In my controller, I wrote 2 methods..
public ActionResult TwitterLogin()
{
var authorizeUrl = TwitterService.Authorize();
if(!String.IsNullOrEmpty(authorizeUrl))
{
return Redirect(authorizeUrl);
}
else
{
return View();
}
}
public ActionResult AuthorizeCallback()
{
TwitterService.AuthorizeCallback();
return View();
}
In Twitter Service
public string Authorize()
{
client = new RestClient(BaseUrl) {Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, CallbackUrl)};
var request = new RestRequest("oauth/request_token", Method.POST);
var response = client.Execute(request);
if(response.StatusCode == HttpStatusCode.OK)
{
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
request = new RestRequest("oauth/authorize");
request.AddParameter("oauth_token", oauth_token);
return client.BuildUri(request).ToString();
}
return String.Empty;
}
public void AuthorizeCallback()
{
var verifier = "123456"; // <-- Breakpoint here (set verifier in debugger)
var request = new RestRequest("oauth/access_token", Method.POST);
client.Authenticator = OAuth1Authenticator.ForAccessToken(ConsumerKey, ConsumerSecret, oauth_token, oauth_token_secret, verifier);
var response = client.Execute(request);
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
}
Now my concern is
Whether i am doing it right ?
Regarding Oauth, from what I understood , we create a request token from the twitter, ask the user to authorize it and use it to get an accesss Token and use it for signing other requests. I wrote this code primarly looking this link
https://github.com/restsharp/RestSharp/blob/master/RestSharp.IntegrationTests/oAuth1Tests.cs
Whether we can write this any better ?
Also somebody could guide me on this , how to use the RestSharp to create OAuth requests to use the API's like twitter. Most of the internet references are based on the custom libs