I am using swagger-js/swagger-client (https://github.com/swagger-api/swagger-js) in an AngularJs SPA to send REST request to a nodeJS server using the TryItOut Executor.
I am using PassportJS for authentication. When I use Angular's $http service with the withCredentials property set to true:
$http({
method: 'GET',
withCredentials: true,
url: 'http://someUrl/someRestPath/' })
, everything works OK - browser cookies are properly set, and every further request is successfully authenticated.
Switching to swagger-js Executor, I cannot get authenticated properly since cookies are not being set. I have tried the following:
var swaggerClient = new SwaggerClient(specUrl)
.then(function (swaggerClient) {
swaggerClient.http.withCredentials = true; // this activates CORS, if necessary
as suggested in the documentation but this has no effect. Although I receive credentials when I authenticate with passport, further requests are unauthorized as cookies are not being set.
Left: cookies not being sent; Right: cookies being set
How to enable this behavior using swagger-js?
I was able to find a solution. The execute method of the Swagger-js client allows us to define our own http method. I simply used angular's $http service with the withCredentials set to true globally in the .config
import Swagger from 'swagger-client';
in .config:
angular.module('app', []).config(['$httpProvider',function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}
And then when you create your swagger client:
Swagger({ url: 'some swagger specification (yaml/json)' }).then(client => {
client.execute({ http: $http, operationId: operationId, parameters: parameters});
Related
I am trying to set a cookie from a response when using the scala play framework. I am creating a cookie called session with its value being a UUID (I do know play comes with its own session management).
I can see the response headers contain the set cookie instruction, but the cookie ins't being set.
Below is the scala code in the action in the controller where the cookie is set
val cookie= Cookie("session",
sessionToken,
httpOnly=true,
sameSite=Some(Cookie.SameSite.Lax))
Ok(Json.toJson(res))
.withCookies(cookie)
// Also tried with .bakeCookies() after the withCookies() call
I can see the cookie header in the response in both FireFox and Chrome. They both show what appears to be a correctly formed cookie in their respective response cookie viewer in their developer tools.
Set-Cookie: session=c0174ed1-ebd3-4a8d-a5b2-5b09a3fe616b; SameSite=Lax; Path=/; HTTPOnly
However, in both browsers the cookie does not get set. I have tried true and false httpOnly, and messing with the maxAge value. I have tried setting the domain to a url and then setting the url in the hosts file as suggested here.
The URL on the react frontend is
http://localhost:49161/login/hydrate
and the endpoint in play is
http://localhost:49162/user/login/rehydrate
I did also try setting the path to /login/hydrate/ on the cookie
The react code on the front end
// Inside async function
// methodStrings[method] = "post" in this request
axios[methodStrings[method]](url, params)
.then(result => {
resolve(result);
})
.catch(err => {
console.error(`Error with request to ${url} - ${err}`);
reject(err.response.status);
});
And then I console.log the result from awaiting the request
Problem is in axios (and/or browser).
Based on this issue Cookies headers are present but Cookies are not stored in browser #1553 you need to set withCredentials Axios config to store cookies in browser after request:
axios.post('domain.com', {
name: 'name',
password: 'password'
}, {
//AxiosRequestConfig parameter
withCredentials: true //correct
})
I'm building a SPA using Angular.js and ASP.NET and I would like to know what is the best way to secure it.
Here is what I need :
I would like to use MVC framework to hide my application only to logged users. So the first thing that users will do before launching the SPA will be to log into the website using a simple login form.
When the Angular app will be launched, it will communicate with my ApiController using REST requests.
I also want my user to be logged out automatically after 20 minutes of inactivity.
I know that REST is supposed to be stateless... but I can't figure how to implement all I need without sessions...
But on the other side, I want to be able to use my WebAPI with a future mobile application. I will have to use Tokens for the authentication on this application.
What is the best way for me to achieve that kind of authentication?
Thanks for your time!
I developed an entire security layer with the same conditions as yours following those very well explained in this post here.
BTW, the token will expire automatically after 20 minutes because when you create it you will set it's expiration date immediately; every time you're going to make a request, the system will check the token exp date with the current date, refusing your token if the time passed. For example this a tipical oauth server configuration with token and refresh token settings:
internal static OAuthAuthorizationServerOptions GetAuthorizationServerOptions(IComponentContext scope)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
ApplicationCanDisplayErrors = true,
TokenEndpointPath = new PathString(Constants.PublicAuth.OAUTH_TOKEN_PATH),
AuthorizeEndpointPath = new PathString(Constants.ExternalAuth.AUTH_ENDPOINT),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(Constants.PublicAuth.TOKEN_EXPIRATION_IN_MINUTES),
Provider = scope.Resolve<AuthorizationServerProvider>(),
AccessTokenFormat = new CustomJwtFormat(),
RefreshTokenProvider = scope.Resolve<SimpleRefreshTokenProvider>()
};
return oAuthServerOptions;
}
The refresh token is also very useful, but you have to manage the token replacement by yourself; for example in our application we pass every API call through a single service that, if the server responds 401 (unauthorized), it will try to request a new token using the refresh token and then it will try the same call again. Only after the second failure you'll be redirected to the login page.
For example:
function executeCallWithAuth(method, url, payload, params) {
var defer = $q.defer();
debug.logf('{0}: {1}', method.toUpperCase(), url);
$http({ method: method, url: url, data: payload, headers: createHeaders(), params: params }).then(
function(results) { defer.resolve(results); },
function(error) {
if (error.status !== 401) defer.reject(error);
else {
debug.warn(`Call to: ${method}:${url} result in 401, try token refresh...`);
auth.refreshToken().then(
function() {
debug.warn('Token refresh succesfully, retry api call...');
$http({ method: method, url: url, data: payload, headers: createHeaders() }).then(
function(results) { defer.resolve(results); },
function(errors) { defer.reject(errors); });
},
function(tokenError) {
debug.warn('Token refresh rejected, redirect to login.');
$state.go('login');
defer.reject(tokenError);
});
}
});
return defer.promise;
}
and
function createHeaders() {
var headers = {
};
var authData = storage.get('authorizationData');
if (authData) {
headers.Authorization = 'Bearer ' + authData.token;
}
return headers;
}
Using Angular the best way to secure a route is "do not create a route". Basically, you need to load the user profile, and only after that you will create the routes only to the pages he can navigate to. If you don't create the route for a page you don't need to secure that page: Angular will automatically send the user to a 404.
I would secure your WebAPI calls with OAuth2 (you can even use the built in Identity 2.0 provider that comes baked in with it). Keep your WebAPI stateless, use SSL (consider a filter to force it), and use the [Authorize] tags to secure you services. On the MVC side, this will have to maintain state and you will want to have the login form get an OAuth2 token from your WebAPI layer and pass that down into Angular. Set the expiration on this to 20 minutes. You can also use the cookies authentication model here since it will need to be stateful on the MVC side, but all ajax calls made to the WebAPI layer by Angular will need to pass the OAuth2 token as a bearer token in the Authorization request header.
I am using the angular.js and csrf (Cross-site request forgery).
I have added the angular cookie file.
script src="angular-cookies.js"
And then, to load it in the application:
angular.module('myApp', ['ngCookies']);
It’s really easy to add the CSRF token in the headers of the $http service. We just need to configure it into the run process of the application, requiring the $cookie and $http services.
.run(function ($http, $cookies) {
$http.defaults.headers.post['x-csrf-token'] = $cookies._csrf;
});
in the browser debug console, I can find the cookie is existing.
However,when the page is loaded, $cookies outputs undefined, it is strange the $cookies object itself is undefined, but obviously I have loaded the cookie.js and inject the module into the application
This is on the node.js server side, which how I set a CSRF token on the cookie
app.use(require('csurf')());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
res.cookie('XSRF_TOKEN', req.csrfToken(),{ maxAge: 900000, httpOnly: false });
next();
});
Thanks for the reminding, I changed the
res.cookie('XSRF_TOKEN', req.csrfToken(),{ maxAge: 900000 });
to
res.cookie('XSRF_TOKEN', req.csrfToken(),{ maxAge: 900000, httpOnly: false });
so the cookie is readable from the browser.
According to angularJS documentation
Your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on the first HTTP GET request.
This cookie must be readable with Javascript, and ideally sent with the index.html page.
When this is done, you should see it in your browser (options / cookies ...)
At this point, you should access it in your angular code.
If this is not the case, may you should try to access it with pure Javascript
_getCookie = function(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++){
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
};
getCookie("XRSF_TOKEN");
Your cookie is probably being set as HttpOnly = true which will set the cookie but will not allow the client to view it therefore it comes back as undefined. You will need to make sure this flag is set to false.
Is there a workaround to sending POST request cross-domain via Angular, besides using a proxy? Below request is refused, ie: OPTIONS , net::ERR_CONNECTION_REFUSED It's just form data I want to submit to friend's local server for school project.
$scope.postJSON = function(){
var objJson = angular.toJson($scope.event);
console.log(angular.toJson($scope.event));
delete $http.defaults.headers.common['X-Requested-With'];
$http({
method: 'POST',
url: 'http://friendslocalserver.com',
data: objJson
}).success(function() {
console.log("POST Json object worked!");
}).error(function(){
console.log("POST Json object failed!");
});
}
You don't need to configure AngularJS for CORS. Your friend's server needs to support CORS requests and probably whitelist your domain. This depends heavily on the HTTP server used.
I was trying to access api running in localhost using angular $resourses, chrome console gives me error saying ERR_INSECURE_RESPONSE.
I tried disabling web security in chrome. still same error. here is the angular factory that i used. How can I bypass this error and test my app.
ImpactPortal.factory('apiFactory', function ($resource) {
return $resource('https://localhost:8443/mifosng-provider/api/v1/client_impact_portal', {}, {
query: {
method: 'GET',
params: {},
isArray: true
}
})
});
Enabling CORS in Angular.js
var myApp = angular.module('myApp', [
'myAppApiService']);
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
A server supporting CORS must respond to requests with several access control headers:
Access-Control-Allow-Origin: "*"
By default, CORS requests are not made with cookies. If the server includes this header, then we
can send cookies along with our request by setting the withCredentials option to true.
Access-Control-Allow-Credentials (optional)
If we set the withCredentials option in our request to true, but the server does not respond
with this header, then the request will fail and vice versa.
Only try this:
Go to https://[your-domain].com and then chrome block you with the famous page:
Your connection is not private
So go down to ADVANVCED and then proceed.
Your certificate is probably self-signed.
Remember to do that for each Chrome session.
You must authenticate first and then send each request along with the auth token.
I am using RestAngular so my settings might look a little different from what you are working on.
This will go in your application config :-
RestangularProvider.setDefaultHeaders({ 'X-Mifos-Platform-TenantId': 'default' });
and something like this will go in your controller/service
var login = Restangular.all('authentication?username=mifos&password=password').post().then(function(user) {
console.log(user);
}, function() {
console.log("There was an error saving");
});
Error 501 (net::ERR_INSECURE_RESPONSE) - 501 Not Implemented
The server either does not recognize the request method, or it lacks the ability to fulfill the request. Usually this implies future availability (e.g., a new feature of a web-service API).
Can you confirm that curl request is working fine
curl -k --user damien#email.com:password https://localhost:8443/mifosng-provider/api/v1/client_impact_portal
If it is working fine:
ImpactPortal.factory('apiFactory', function ($resource) {
return $resource('https://localhost:8443/mifosng-provider/api/v1/client_impact_portal', {}, {
query: {
method: 'JSONP',
params: {},
isArray: true
}
})
});
Try following this tutorial that consume an external API: http://www.toptal.com/angular-js/a-step-by-step-guide-to-your-first-angularjs-app