I am using Asp.net Identity for token based authentication in my web api application. The problem is I have to perform some operations after the token is generated and the user is authenticated and before the redirection to client side occurs.
I have a login page which uses /token token authentication . Once the token is issued i need to keep the user and token values in a session. [this session will be used to show online users.]
Client request
$('#btnLogin').click(function () {
$.ajax({
// Post username, password & the grant type to /token
url: '/token',
method: 'POST',
contentType: 'application/json',
data: {
username: $('#txtUsername').val(),
password: $('#txtPassword').val(),
grant_type: 'password'
}
});
Server side
[HttpPost]
public void Login()
{
OnlineUsers user = new OnlineUsers();
var users = (HttpContext.Current.Session["ActiveUsers"] as
List<OnlineUsers>) ?? new List<OnlineUsers>();
users.Add(user);
HttpContext.Current.Session["ActiveUsers"] = users;
}
I need to call this controller method after the token is issued and use is authenticated.
Is there any solution to this?
If you want to intercept generation of the token think you have to customize the aspnet identity behaviour
https://msdn.microsoft.com/en-us/library/microsoft.aspnet.identity.usermanagerextensions.generateusertoken(v=vs.108).aspx
With Web API using session is not a good approach, to keep user's information in client side you can use browser's localstorage. Once the user authenticated via your login api controller, you can return the user's required info as json to client, then you can keep it to browsers. Web API is by default stateless so i think session is not suitable with it, additional burden on the client. Storing session state on the server violates the stateless constraint of the REST architecture. So the session state must be handled entirely by the client.
Web API
[HttpPost]
public IHttpActionResultLogin()
{
OnlineUsers user = new OnlineUsers();
user=YourUserDetailsMethod();
return Ok(user);
}
Client:
$('#btnLogin').click(function () {
$.ajax({
// Post username, password & the grant type to /token
url: '/token',
method: 'POST',
contentType: 'application/json',
data: {
username: $('#txtUsername').val(),
password: $('#txtPassword').val(),
grant_type: 'password'
},
success: function(response){
window.localStorage.setItem('userInfo', response);
$('#UserName').val(window.localStorage.getItem('userInfo').UserName);
}
});
Related
I'm trying to share a cookie between two ASP.NET mvc 6 apps :
The first one is a web api app and it should represent the main domain "localhost".
Second one is an empty mvc website client app, with one index.html that calls the web api via simple ajax. this app represents the subdomain "cleint.lohalhost".
For somereason, my browser is unable to set the cookie on the client app. Details below :
-Set cookie header generated by web api
Set-Cookie:mytoken=mytokenvalue; domain=.localhost; path=/; samesite=lax
-Ajax call :
$.get({
url: 'http://localhost:5004/api/values',
success: function (response) { console.log(response); },
error: function (error) { console.log(error); },
xhrFields: {
withCredentials: true
},
crossDomain: true
});
-And finally, the code that sets the cookie on the web api app :
[HttpGet]
public IEnumerable<string> Get()
{
Response.Cookies.Append("mytoken", "mytokenvalue", new CookieOptions() { Domain = ".localhost" });
return new string[] { "value1", "value2" };
}
-I use chrome browser if it's relevant.
I appreciate any kind of help, thank you.
It turned out my browser was still hungry for another '.' on the domain name, so I ended up replacing 'localhost' (on both domain names) with 'myapp.dev' and it worked.
It is also important to note that from the api side, I had to send the domain name cookie option with the value '.myapp.dev' instead of 'client.myapp.dev'
Editing to add in extra details.
I have a web project that I effectively use as an authorization server (e.g. example.com). I then have a few web sites that sit as sub domains (e.g. sub1.example.com, sub2.example.com). I am currently unable to get the .AspNet.Cookies cookie to save on the subdomains when logging in to the authorization server. I can see the cookie come back in the response but it's not being set.
I have searched and tried various solutions such as setting the CookiePath and CookieDomain. I have verified the Machine Key in the Web.config file matches between all sites. This is currently how I am enabling Cookie Authentication:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = new PathString("/Account/Login"),
CookieDomain = ".example.com",
});
I have CORS enabled on the authorization server and I am able to receive the Bearer token when I log in, I just can't seem to get the cookie to be saved.
Thanks in advance.
Edit: I read somewhere that the ARRAffinity cookie could mess with things so I disabled that as well. I am still unable to see the cookie in the subdomain.
Edit 2: Adding the ajax call as requested in the comments (password and domain have been altered for consistency with the post):
$.ajax({
url: 'https://example.com/auth/token',
method: 'POST',
data: {
grant_type: 'password',
username: 'admin#example.com',
password: '************'
},
crossDomain: true
});
I'm going to take a shot at the answer here.
By default, when making cross-site ajax requests, browsers will ignore cookies. More information on this here.
To allow the use of cookies, you must set withCredentials to true in your requests, like so (more information here):
$.ajax({
url: 'https://example.com/auth/token',
method: 'POST',
data: {
grant_type: 'password',
username: 'admin#example.com',
password: '************'
},
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
I've tested this locally and this should be enough if the only thing you need is to authenticate with example.com and then keep using the cookie while interacting with sub1.example.com.
If you also want to make a request with the cookie to example.com and do not want to have your responses ignored by the browser, according to this link you should make sure that example.com also returns the header Access-Control-Allow-Credentials: true.
I am trying to write a program that will login a user programatically into a ArcGIS portal.
Here is my scenario:
User logs in to application A, user clicks a link to the ArcGIS portal - I do not want them to have to login to the portal when they click that link because that have already logged into application A.
SO
I would like:
User logs in to application A, user clicks a button containing the portal link as an argument and redirects to application B. Application B logs the user into portal and redirects them with the link from application A - the user is redirected without being prompted to login.
The portal is using portal tier authentication and I am using javascript but I could also use .NET/C#
UPDATE:
My current solution looks like this:
var url = "https://PORTAL_DOMAIN/portal/sharing/rest/generateToken";
var redirect = "https://PORTAL_DOMAIN/portal/home/webmap/PLACE_I_WANT_TO_REDIRECT_TO";
var params = {
'username': "username",
'password': "password",
'client': "referer",
'referer': redirect,
'expiration': 60,
'f': 'json'
};
$.post(url, params)
.done(function (data) {
var tokenHolder = JSON.parse(data);
var token = tokenHolder.token;
$('body').append(token);
document.cookie("esri_auth", token);
window.location = redirect;
});
This code gets me a token from the rest service - I try to store it has a cookie but it doesn't persist.
I have also tried using a C# web request and a credential cache to generate the credentials but I didn't save the code I was using.
SOLVED IT:
Okay, so my original post was not far off from what I needed. My missing link was the cookie formatting and properties.
Also its important to mention that you cannot run this locally but you have to has access to the portal server and it only works once published out.
IN THE CODE BELOW:
ENCODED COOKIE - is an URL encoded json object. I signed into my portal and just copied the cookie format (using chrome dev tools) and concatenated the generated token into the cookie and redirected. ALSO I had to set the domain,expire, and path properties of the cookie.
var url = "https://PORTAL_DOMAIN/portal/sharing/rest/generateToken";
var redirect = "https://PORTAL_DOMAIN/portal/home/webmap/PLACE_I_WANT_TO_REDIRECT_TO";
var params = {
'username': "username",
'password': "password",
'client': "referer",
'referer': redirect,
'expiration': 60,
'f': 'json'
};
$.post(url, params)
.done(function (data) {
var tokenHolder = JSON.parse(data);
var token = tokenHolder.token;
var domain = ".PORTAL_DOMAIN";
document.cookie = "esri_auth=ENCODED COOKIE;expires=Session;domain=" +domain + ";path=/;secure";
window.location = redirect;
});
I have a WebApi 2 and a MVC Web project in the same solution running on different IIS ports. After recieving my Oauth token using jQuery AJAX I still get a 401 Unauthorized error message when trying to call an authorized Controller method.
Startup:
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
ConfigureWebApi(httpConfig);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(httpConfig);
}
CustomOAuthProvider:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<UserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
// checks with context.SetError() results.
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Role, "User"));
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
Thinks I've tried from I get "Authorization has been denied for this request." error message when using OWIN oAuth middleware (with separate Auth and Resource Server):
Updating all the Owin packages to latest version (Web project does not use any Owin functionality so it is not installed here).
Api and web are different projects but on same machine, so same machinekey.
OAuth Token configuration comes before the WebApi configuration in Startup.cs.
Claims are made: oAuthIdentity consist out of a role and an admin claim (http://schemas.microsoft.com/ws/2008/06/identity/claims/role: User)
Everything else works as expected (web api, cors, token generation,...), what am I doing wrong? (There is a lot of code involved, so let me know if I need to place an other piece of code from my projects.
EDIT:
Ajax call (Solution by jumuro):
var token = sessionStorage.getItem(tokenKey); // Same as the generated login token
$.ajax({
type: 'POST',
// Don't forget the 'Bearer '!
beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', 'Bearer ' + token) },
url: 'http://localhost:81/api/auth/test', // Authorized method
contentType: 'application/json; charset=utf-8'
}).done(function (data) {
//
});
You have to include an Authorization header with the bearer token in the ajax call. Please see this reponse as an example.
I hope it helps.
I'm currently programming a messaging system for a website using C# .NET as backend.
The messaging system is very similar to Facebook's web interface, which allows you to "chat" with another person, sending the messages via AJAX.
I've created a webservice (C#) that handles the actual sending message bit. I'm using JQuery to activate that service using the following code:
// generic webservice used to retrieve count from db
function SendMessageAJAX(taskID, sendeeID, sendeeType, recipientId, recipientType, content) {
$.ajax({
type: "POST",
url: "/WS/UIServices.asmx/SendMessage",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ 'content': content, 'SendeeType': sendeeType, 'SendeeId': sendeeID, 'RecipientType': recipientType, 'RecipientId': recipientId, 'taskID': taskID }),
dataType: "json",
success: function (msg) {
// refresh chat area
LoadMessages(false);
},
error: function () { alert("error"); }
});
}
As you can see, I'm passing both the sendee / recipient info in my request. Obviously the code is very dangerous, as anyone can modify these values and impersonate any user.
I have the current logged-in user in a SESSION variable on my server side, but the code runs async, meaning the session variable is NOT defined when it runs.
What would the best way to safely run these actions via AJAX?
There are two options:
You can either encrypt your ajax requests like this code project article OR use SSL for website.
Implement your server code in an IHttpAsyncHandler and also implement IRequiresSessionState so you have access to the session.
http://msdn.microsoft.com/en-us/magazine/cc164128.aspx
is it possible to run async call to set a session in ASP.NET?
Using ssl is always preferred but other options are also viable if you have time to implement.