Complete OpenIDConnect auth when requesting via Ajax - javascript

The normal OpenIDConnect server works like:
You go to a.com/secure-resource
You get a 302 back from the server
Your browser handles it and sends you to the identity server
You login there
It sends you back to a.com via a POST
You get logged in on a.com and get a.com/secure-resource back on your browser.
However I have a scenario that I'm trying to solve but I need your help.
The user is already logged in on idServer
The user is logged in on a.com
The user is NOT logged in on b.com
We need to send an ajax call to web server b.com (from another domain a.com)
b.com is configured to use OpenIDConnect.
But because the request to b.com is via Ajax, user cannot be redirected normally to idServer. (all we get in response is a 302)
We can go ahead and handle the 302 via Ajax (I'm still not sure whether that would work, security-wise).
BUT
Is there any scenario in IdentityServer/OpenIDConnect that is designed for these situations?

With IdentityServer in this scenario you setup server b.com to use Bearer Token Authentication, you then need to use the access token provided for a.com in the headers of your Ajax call
$.ajax({
url: 'http://b.com',
headers: {
Authorization: "Bearer " + Your Access Token
}
})
The JavaScript IdentityServer Client samples have ways of retrieving the Token from the Identity Server, see here
In a controller you can get the user and the token like this
// Get the claims values
var token= (User as ClaimsPrincipal).Claims
.Where(c => c.Type == "access_token")
.Select(c => c.Value).SingleOrDefault();
In other parts of your application you can use this
//Get the current claims principal
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
// Get the claims values
var token = identity.Claims.Where(c => c.Type == "accept_token")
.Select(c => c.Value).SingleOrDefault();

Related

Javascript how to redirect to another website and send the JWT as bearer token as well

I need to redirect to another website and send the JWT as a bearer token as well.
Can I use fetch to do it?
Something like this:
$( document ).ready( function() {
document.getElementById('Aother-Website-Link').addEventListener('click', event => {
event.preventDefault();
fetch('Another-Website-Link',
{
mode: "cors",
method: "GET",
headers: {
'Authorization': `Bearer ${jwt}`
}
})
.then(() => {
window.location.href = 'Another-Website-Link';
})
.catch(error => {
console.error(error);
});
});
No, you can't do this. First, cookies cannot be transfered nor set across different domains, cookies can only be set if the server is sitting on the same domain or a subdomain.
If your website is website1.com and you make a fetch request to backend2.com, backend2.com cannot set any cookies for your website. If you wanted to set cookies on your website, your website should either make a fetch request to the same domain (website1.com/api/something) or a subdomain like api.website1.com.
With this in mind, no, you cannot redirect and transfer cookies to another website.
If you have access to the site there might be a solution. You can set up a custom route on the site you want to redirect to, call it something like /redirectCallback or something like that, and then you can redirect the user and either pass in your cookies via an url query (/redirect callback?cookie1Val=...) so that the website you want to redirect to can parse those values, send them to their server and then their server can set the same cookies, or, if you want more security and you also happen to have a database set up, you can create a temporary switchSession with and id and cookieValue fields on your server right before you redirect and send it's id via url query (/redirectCallback?redirectSessionId=...) and then the website can again parse it, send it to their server, their server will parse the data on your database then delete the redirectSession and send your cookies up to the client again (You can implement signing/veryfing if you want even more security).
I hope it helped!
Edit: spelling.

How to extract status code of a protected web page with Node.js backend?

I am trying to only access a webpage's returned status code but the page is proctected by a login process. I already have the credentials but I cannot directly send http request and see the status code because it returns HTML of login page. I need to find a way such that when I send http request to that web service, it should go through those login process and return me only status code. An advised way is that using scripted browser, but still even if I successfully login to that page and display it, how can I send status code to my local backend ?
If the logon process consists of just giving the correct username and password, you can "fake" it with a single request like
fetch("https://protected.page/login", {
method: "POST",
headers: {"content-type": "application/x-www-form-urlencoded"},
body: "username=you_know_it&password=you_know_it&submit=Logon"
}).then(response => response.status);
But if login involves one-time tokens or captchas, this is of course not sufficient.

How can I safely implement my login routes?

I am currently making a login and registration system as a project for a website, but I am uncertain of how I can safely implement the routes/logic for it. Currently, in my client side code I send a fetch request to my login or register route, and do the logic there, and then send a JSON object back, which is manipulated whether there is an error or not, and I have implemented CORS and Header (Origin and Referrer headers) authorization middleware within my POST routes, so no external script can mass produce users/login [aka brute force]. Is there another safer and better way of handling this?
What you want to do is, to use either JWT or Session Cookies.
Algorithm ->
User visits the /login page
User enters user id and password
U send a fetch or Axios request to your servers POST API route
axios(`/<api_route_here>`,{
method: "POST",
headers: {
Authorization: `Basic ${btoa(`${username}:${password}`)}`
}
})
In the above code, we are encoding the username and password with base64 and then sending them back to the server with the Authorization header. username:password
Note: Make sure u are using an HTTPS connection
In the server u decode the Authorisation value from the header. Then create a hash of the password with username as its salt. Then query your database if the hash matches the user's password's hash.
We are hashing here to prevent leaking passwords in case of a database hack.
It is obvious, that you will store the password for the user using the same hashing technique.
If the query is positive then send a 200 response else send a 401.
In the Axios promise resolution check for the headers status code to figure out if the sign-in was successful
To prevent asking the user their password every time. U must use session cookies. i.e in step 4 if the password is correct add a Set-Cookie header property with a session token value. Store that session token to your database. Next time you API is hit, check for the cookie. if the cookie exists then check that session token with your database. otherwise, send a 401
To prevent route spamming, implement a captcha.

How to redirect user to another page after login that base on JWT-token?

I use a JWT-token for authentication of users in my ASP.NET Core Web App.
The process of authentication has following steps:
Client send an ajax request to the server url with params login/password to get access token
Server get request and send a response with access token and token type
Client get server response and save token in a session storage to use token for requests later
When client has a token he should add token type and token to header of every request like the following sample (jQuery.ajax() headers section):
headers: { 'Authorization' : tokenType + ' ' + token }
Client redirects user from login page to main page. In JavaScript I can make it with the following code:
Code:
window.location.replace('[URL_TO_MAIN_PAGE_HERE]');
or
window.location.href = [URL_TO_MAIN_PAGE_HERE];
However I has a problem that I can't set a header for the request above.
How can I redirect user to main page after login if I use access token for authentication?
Additional info:
App is not SPA.
Once you save the token in browser's session/local storage, it will be available for any further "API" requests to the server. When you request for a page, by doing a server.transfer / request.redirect / location.href etc, you cannot provide custom headers of anytype.
So what's the available options.
Lets say post login, you redirect user to a page (using any of methods), which lists out some entities. The listing page does (or should do) an ajax request to the server (upon load, in the header script) to fetch the data. At this step, you can read the auth token and include in the request; which the server can validate.
Any subsequent requests will be done in same manner, whenever you request any resource, include the token in the http request.
If your API can return processed html, then you can do a GET request to that and pass the auth token, retrieve the html and include it within your page...

How to store the access_token gained from OAuth for later use?

I'm attempting to get and store an access token from the Pocket API using Node.js. I am able to get the request token, redirect to the Pocket login page, redirect back to my site, and finally able to exchange the request token for an access token.
But that's where my problem lays. I don't know how I should go about actually storing the token, and without it I am unable to make API calls (of course). Here's the relevant code:
//called when Pocket API redirects back to /getAccessToken
function getAccessToken(response, requestToken) {
restler.post("https://getpocket.com/v3/oauth/authorize", {
headers: { "Content-Type" : "application/json",
"X-Accept" : "application/json" },
data : JSON.stringify({consumer_key:CONSUMER_KEY,code:requestToken})
}).on("complete", function(data, res) {
if(res.statusCode == 200) {
var accessToken = data.access_token;
console.log("Access granted: " + accessToken);
//BUT HOW DO I STORE THE ACCESS TOKEN FOR USE OF API CALLS ??
}
response.writeHead(307, {Location: DNS}); //go back to site
response.end();
});
};
I was thinking I should store the accessToken on the client side, but I don't actually know how to go about doing that. I've tried using cookies, but that didn't seem to work. Of course, I may have just implemented them wrong.
Your help is much appreciated.
How you store the access token usually depends on where you will be using the API.
I usually like to persist tokens in the database (like MongoDB) on the User document they are associated with, and then my web client can ping my server (via a RESTful endpoint) if it needs the token for anything. This way if the user clears all that state on the browser you don't have to go through the entire OAuth flow again.
you should probably make the cookies thing work. option 2 is to use localStorage but if you're struggling with cookies i wouldn't try going down that path - it gives you more control of when the tokens are sent across the wire but also requires a lot more work to make your serverside and clientside code coordinate.

Categories