I am doing a login to a server (different IIS then the one which the client is), the response of this login is a cookie
Set-Cookie:session-token=7ed240cd-fd41-464c-9ccd-d43097ef4d7c; domain=x.x.x.x; path=/
the login is done via JQuery POST, the server is ODATA server - I am initilizing breeze with
breeze.config.initializeAdapterInstances({
modelLibrary: "backingStore",
dataService: "OData"
});
var breezeDataServiceSettings = {
serviceName: serverUrl + 'odata',
hasServerMetadata: true
};
var dataService = new breeze.DataService(breezeDataServiceSettings);
manager = new breeze.EntityManager({ dataService: dataService });
manager.metadataStore.fetchMetadata(dataService).then(succeded, failed);
so far all is ok, however when I am sending requst to get entity (also fetch metadata) the cookie isnt being sent, i have tried to send request with JQuery and the cookie is sent
also I have tried to add headers to the ajax breeze adapter (add the cookie) but it is being ignored.
How can it be solved?
I think I found the solution -
in datajs-1.1.1 under
request: function (request, success, error)
when creating the createXmlHttpRequest object I added
xhr.withCredentials = true;
Related
I'm trying to implement an authentication scheme in my app. The controller, more specifically the method, responsible for checking user's credentials and generating jwt which it has to put into the httponly cookie afterward looks as follows
[HttpPost]
[Route("authenticate")]
public async Task<IActionResult> Authenticate([FromBody] User user)
{
var response = await _repository.User.Authenticate(user.Login, user.Password);
if (!response) return Forbid();
var claims = new List<Claim>
{
new Claim("value1", user.Login)
};
string token = _jwtService.GenerateJwt(claims);
HttpContext.Response.Cookies.Append(
"SESSION_TOKEN",
"Bearer " + token,
new CookieOptions
{
Expires = DateTime.Now.AddDays(7),
HttpOnly = true,
Secure = false
});
return Ok();
}
I tested this method in Postman - everything works gently and correctly in there. The cookie is being created as well. Moreover, recently I created an app using Angular where I was using the same authentication method, but with Angular's HTTP module the cookie was being created all the time. Here is what that method looks like in my React app with the usage of Axios
export const authenticate = async (login, password) => {
return await axiosLocal.post('/api/auth/authenticate',
{login, password}).then(response => {
return response.status === 200;
}, () => {
return false;
});
Everything I'm getting in response trying to log in is response code 200. I'm pretty sure it's something about Axios's settings.
Also if someone's curios the variable "axiosLocal" contains the baseURL to the API.
- Update 1
Ok. If I'm not mistaken in order to set a cookie from the response I have to send all the requests with { withCredentials: true } option. But when I'm trying to do that the request is being blocked by CORS, although I had already set a cors policy which has to allow processing requests from any origin like that
app.UseCors(builder => builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials());
I just had the same issue. I fixed it.
Problem:
In browsers, the httpOnly cookie was received and not returned to the server
In Postman working
// Problemable server code for settings httpOnly cookie
Response.Cookies.Append("refreshToken", refreshToken.Token, new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddDays(7),
});
Solution:
On the server .AllowCredentials() and
.SetOriginAllowed(host => true) or
.WithOrigins("https://localhost:3000")
On the client (react, axios) withCredentials:true in the headers
If still not working open the Network tab in DevTools in Chrome(current v.91.0.4472.124), select the failed request and when you put the mouse over the yellow triangle you can see very detailed information why the cookie is blocked.
// End server code for setting httpOnly cookie after following the DevTools warnings
Response.Cookies.Append("refreshToken", refreshToken.Token, new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddDays(7),
IsEssential=true,
SameSite=SameSiteMode.None,
Secure=true,
});
Finally solved. Passing .SetIsOriginAllowed(host => true) instead of .AllowAnyOrigin() to CORS settings with { withCredentials: true } as an option in Axios request helped me.
I have a Django web app that is using the Django REST framework to generate various API endpoints.
I can ensure only logged in users can view/read these endpoints, but now I am at the stage of development where I want users to post to the API using tokens. I have successfully done this, however, I have hard-coded the users token into the post request in Javascript... This worked for testing but obviously is not a good final solution.
Is it possible to request the current users token somehow? Could I then include this token in the POST request head automatically?
Thanks for any help/feedback in advance!!
EDIT:
I think I am close, but I am getting a few errors in my chrome console, and still can't retrieve token.
Console Errors:
toggleScript.js:25 Uncaught DOMException: Failed to execute
'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
at getToken (http://127.0.0.1:8000/static/defaults/toggleScript.js:25:7)
at manageDefaults
(http://127.0.0.1:8000/static/defaults/toggleScript.js:62:5)
at HTMLInputElement.onclick (http://127.0.0.1:8000/defaults/:1:1)
getToken # toggleScript.js:25
manageDefaults # toggleScript.js:62
onclick # (index):1
toggleScript.js:24 POST http://127.0.0.1:8000/api-token-auth/ 415
(Unsupported Media Type)
I have a button when pressed, will trigger the function to retrieve the token, and this is what is causing the error stack above.
toggleScript.js
function getToken(){
var xhr = new XMLHttpRequest();
var url = 'http://127.0.0.1:8000/api-token-auth/';
xhr.open("POST", url, true);
var data = JSON.stringify({"username": "myusername", "password": "mypassword"});
xhr.send(data);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log(json.token);
}
};
}
Django Rest Framework provides an API endpoint for requesting a user's token, given a username and password. You can wire the view into your urls.py:
from rest_framework.authtoken import views
urlpatterns += [
url(r'^auth-token/', views.obtain_auth_token)
]
Then when you POST a valid username and password to that view it will return the token in a JSON response:
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Your app can then store that and send it in subsequent requests.
An example of retrieving the token using JQuery (assuming the view was mapped to the path ^auth-token/ in your urls.py):
$.post('/auth-token/', { username: 'admin', password: 'whatever' }, function(data) {
// Token available as data.token
});
If you try and post to the auth-token view from within an already authenticated session, Django will likely reject the request with a CSRF token missing or incorrect response. You should either ensure that the session is not authenticated when you retrieve the token, or you could potentially include the X-CSRFToken header in the request. You'd need to extract the value from the csrftoken cookie. For example (using JQuery and the JQuery Cookie plugin):
$.ajax({
url: "/auth-token/",
type: "POST",
headers: {
"X-CSRFToken": $.cookie("csrftoken") # Extract the csrftoken from the cookie
},
data:{ username: "admin", password: "whatever" },
dataType:"json"
}).done(function(data) {
// Token available as data.token
});
More info on obtaining an auth token here
I am using AngularJS and trying to work with Google's reCAPTCHA,
I am using the "Explicitly render the reCAPTCHA widget" method for displaying the reCAPTCHA on my web page,
HTML code -
<script type="text/javascript">
var onloadCallback = function()
{
grecaptcha.render('loginCapcha', {
'sitekey' : 'someSiteKey',
'callback' : verifyCallback,
'theme':'dark'
});
};
var auth='';
var verifyCallback = function(response)
{
//storing the Google response in a Global js variable auth, to be used in the controller
auth = response;
var scope = angular.element(document.getElementById('loginCapcha')).scope();
scope.auth();
};
</script>
<div id="loginCapcha"></div>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
So far, I am able to achieve the needed functionality of whether the user is a Human or a Bot,
As per my code above, I have a Callback function called 'verifyCallback' in my code,
which is storing the response created by Google, in a global variable called 'auth'.
Now, the final part of reCAPCHA is calling the Google API, with "https://www.google.com/recaptcha/api/siteverify" as the URL and using a POST method,And passing it the Secret Key and the Response created by Google, which I've done in the code below.
My Controller -
_myApp.controller('loginController',['$rootScope','$scope','$http',
function($rootScope,$scope,$http){
var verified = '';
$scope.auth = function()
{
//Secret key provided by Google
secret = "someSecretKey";
/*calling the Google API, passing it the Secretkey and Response,
to the specified URL, using POST method*/
var verificationReq = {
method: 'POST',
url: 'https://www.google.com/recaptcha/api/siteverify',
headers: {
'Access-Control-Allow-Origin':'*'
},
params:{
secret: secret,
response: auth
}
}
$http(verificationReq).then(function(response)
{
if(response.data.success==true)
{
console.log("Not a Bot");
verified = true;
}
else
{
console.log("Bot or some problem");
}
}, function() {
// do on response failure
});
}
So, the Problem I am actually facing is that I am unable to hit the Google's URL, Following is the screenshot of the request I am sending and the error.
Request made -
Error Response -
As far as I understand it is related to CORS and Preflight request.So what am I doing wrong? How do I fix this problem?
As stated in google's docs https://developers.google.com/recaptcha/docs/verify
This page explains how to verify a user's response to a reCAPTCHA challenge from your application's backend.
Verification is initiated from the server, not the client.
This is an extra security step for the server to ensure requests coming from clients are legitimate. Otherwise a client could fake a response and the server would be blindly trusting that the client is a verified human.
If you get a cors error when trying to sign in with recaptcha, it could be that your backend server deployment is down.
I am using the request package to create my server side requests. I wrote authentication middleware that checks for a cookie/session id for all requests. Therefore, is there a way I include the user's cookie as part of the request? Here is my current code:
var cookie = parseCookie.parseCookie(req.headers.cookie);
request('http://localhost:3000/users/api', function(error, response, body) {
console.log(body); //this console.logs my login page since requests w/o valid cookies get redirected to login
res.render('../views/admin');
});
Currently, this returns 'no cookie found' in the console. However, if I turn off my authentication middleware, the code above works as intended.
Additional info:
The cookie I want is the end user's cookie located on the browser. The end user's cookie is created by the app whenever the user logs in.
Update - solution attempt 1:
I tried this from the documentation:
var cookie = parseCookie.parseCookie(req.headers.cookie);
var cookieText = 'sid='+cookie;
var j = request.jar();
var cookie = request.cookie(cookieText);
var url = 'http://localhost:3000/users/api';
j.setCookie(cookie, url);
request({url: url, jar: j}, function(error, response, body) {
request('http://localhost:3000/users/api');
});
However, the console is still returning 'no cookie found'
Can someone help?
Thanks in advance!
Let me explain about cookies and that will probably show you why it's hard to get the cookie you want.
When your user's browser logs into http://localhost:3000, that server creates a login cookie and returns it as part of the login response.
When the browser receives that cookie, it saves that cookie persistently within the browser and it associates that cookie with the http://localhost:3000 domain and port.
When the user again makes a request to http://localhost:3000, the browser sends all cookies it has previously saved for that particular domain and port with the request to the server.
When the server receives the request, it can examine any cookies that are sent with the request.
When the browser then makes a request to a different server or even the same server, but on a different port, the browser does NOT send the previously saved cookies with that request because those cookies belong to a different server and port. The browser goes to great security lengths to send cookies only to the servers that the cookies belong to. Since cookies often provide login access, you can clearly see why it's important that things like login credential cookies are not sent to servers they should not be sent to.
Now, on to your node.js code. You show a block of node.js code that is trying to access the same http://localhost:3000 server. But, the cookies are stored in the user's browser. Your node.js code cannot get them from the browser as the browser guards them and will only reveal them when the browser itself sends a request to http://localhost:3000.
If you do actually have the right cookie in your node.js code, then you can set it on your request like this:
request({url: 'http://localhost:3000/users/api', headers: {Cookie: somedataHere}}, function(error, response, body) {
console.log(body); //this console.logs my login page since requests w/o valid cookies get redirected to login
res.render('../views/admin');
});
Relevant documentation for custom headers in the request module is here.
Answer:
var cookie = parseCookie.parseCookie(req.headers.cookie);
var cookieText = 'sid='+cookie;
var options = {
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'.
'host': 'localhost:3000',
'cookie': cookieText //this is where you set custom cookies
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
}
request(options, callback);
When I set up a websocket connection from JavaScript, how can I authorize that it is a legit user on the serverside? I am using JSON Web Tokens and when doing regular calls to REST backend I automatically add an Authorization: Bearer (JWT..) header on AngularJS and then check that on the server side to see if a user is logged in. How can I do that when upgrading the connection to a websocket connection? I am afraid that some people with connect to the server requesting a websocket connection and spoof some of the users id's and receive their messages without being logged in to the service.
I request a websocket connection like this:
var conn = new WebSocket("ws://localhost:8080/api/ws");
conn.onclose = function (e) {
console.log("disconnected");
};
conn.onopen = function (e) {
console.log("connected");
};
conn.onmessage = function (e) {
console.log(e.data);
};
On the first part, is that a GET request or a POST request? Can I add parameters to the url and check them on the serverside? For example:
var conn = new WebSocket("ws://localhost:8080/api/ws/token/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ");
Or is this not a good idea? I also thought sending a JWT would be a good idea because I would be able to extract the user_id from the JWT and associate a websocket connection to a specific user.
How can I solve this problem?