for one web app I'm using Backand.com as MBAAS .
I created a data model and linked all tables.
If I query the default REST APIs there is no problem at all, however if I try to call custom action or custom query the server randomly answer with error 404 not found....
For example I've this custom action named: getCommentsByEvent
'use strict';
function backandCallback(userInput, dbRow, parameters, userProfile) {
// write your code here
return $http ({
method: 'GET',
url: CONSTS.apiUrl + '/1/query/data/getCommentsByEvent',
params: {
parameters: {
event_id: {{event_id}}
}
},
headers: {"Authorization": userProfile.token}
});
}
From the web App I use this code to call it via REST
service.commentsByEvent = function(p, s, r) {
$http({
method: 'GET',
url: Backand.getApiUrl() + '/1/objects/action/comments/?name=getCommentsByEvent',
params: {
parameters: {
event_id: p.eventId
}
}
}).then(s, r);
};
The custom action is written correctly because if I try to call it directly from test page of Backand.com it works without any problem, however if I try from my web app 80% of the time it does not work, 20% it works
This is the error:
Object {data: "Action not found, or is not on demand", status: 404, config: Object, statusText: "Not Found"}
Just to know:
- the action does not have security profile or role or others;
- before to call this action web app call the signin API to get the token
- the action does not need the auth token because is public
To call the signin API I made this service:
service.signin = function(email, password) {
//set the app name of Backand. In your app copy this to .config section with hard coded app name
Backand.setAppName($rootScope.appName);
//call Backand for sign in
return Backand.signin(email, password);
};
service.signout = function() {
return Backand.signout();
};
Any idea?
Thanks
Michele
Fixed!
It was a very dummy issue related with the "App name" created on Backand.com website....
On Backand.com I created an app with all name in uppercase like "MYAPP" so when I configured the SDK on Angular I wrote
Backand.setAppName("MYAPP);
However Backand.com display the same name on the website, but during the configuration it require a LOWERCASE ONLY name! Nobody, even in customer care, knew this requirements so it was very difficult to understand how to fix... Yesterday I did a try in this way and it worked...
Backand.setAppName("myapp");
To sum up, if you have an error 404 and the URL is correct, there is no authentication issue check if the APP NAME is ONLY LOWERCASE.
Related
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 having a very difficult time getting authenticated API requests to GitHub to work. I have created an authorized application in GitHub and connected it to my Auth0 account. I have no problems getting a user signed in using their GitHub account but once they are signed in I cannot make authenticated requests to the GitHub API (I am trying to set a GitHub webhook in one of the user's GitHub repos). All my requests are rejected for having incorrect credentials.
I have the JWT issued by Auth0 being sent along in each request to the GitHub API endpoint but it appears as though this is not sufficient. The Auth0 profile that comes back from my user seems to have an access_token in it, but sending this along does not work either.
Here is what my Auth0 login code looks like (using the Angular API):
angular.module('myApp').controller('LoginCtrl', ['$scope', '$http', 'auth', 'store', '$location',
function ($scope, $http, auth, store, $location) {
$scope.login = function () {
auth.signin({
authParams: {
responseType: 'token' // I think this is the default but just in case
}
}, function (profile, token) {
// Success callback
store.set('profile', profile);
store.set('token', token);
$location.path('/');
}, function () {
// Error callback
console.debug("error logging in");
});
};
}]);
This works fine. They authorize the GitHub application tied to my organization's Auth0 account with its requested permissions without issue and land back in my application and I then have access to an Auth0 profile tied to their GitHub account, but then if I try and make an authenticated request to the GitHub API on their behalf:
var username = auth.nickname;
var repo = "some_user.github.io"; // todo: get repo from setup process
var url = "https://api.github.com/repos/" + username + "/" + repo + "/hooks/";
var conf = {
name: "web",
active: true,
config: {
"url": "https://webtask.it.auth0.com/api/run/wt-my-container_com-0/echo?webtask_no_cache=1",
"content_type": "json"
}
};
$http.post(url, conf).success(function(data, status) {
console.log("post successful:");
console.log(status);
console.log(data);
});
... GitHub rejects the request, either saying the request resource doesn't exist (to prevent private data leakage) or that I supplied bad credentials, depending on different variables (if I try supplying the "access_token" field provided in their Auth0 profile as a query param or supply my Auth0 application's client secret, etc).
I have scoured the documentation of both Auth0 and GitHub trying to figure out what the correct procedure is (for example, do I need to implement the whole OAuth2 token flow myself? it seems like Auth0 should be doing that for me) but nothing I have tried so far works, and nothing on Google has pointed me in the right direction. I have tried a number of other methods of doing this without success but I don't want to make this post too much longer. Any help would be greatly appreciated.
I figured it out. There were two problems: one, a trailing slash had crept in on the end of my API call to the GitHub endpoint, which evidently breaks something and causes GitHub to reject the request, and second, I had set things up to send along the Authorization header with every request as per the Auth0 guide here: https://auth0.com/docs/client-platforms/angularjs, specifically this part:
myApp.config(function (authProvider, $routeProvider, $httpProvider, jwtInterceptorProvider) {
// ...
// We're annotating this function so that the `store` is injected correctly when this file is minified
jwtInterceptorProvider.tokenGetter = ['store', function(store) {
// Return the saved token
return store.get('token');
}];
$httpProvider.interceptors.push('jwtInterceptor');
// ...
});
But GitHub does not like that since it does not contain the token it is expecting and will reject the request if it sees it. Once I removed the trailing slash and removed the above code, everything started working as expected.
Look at this gitHub page. It is something like this with angular:
//'common' will add the headder to every request.
$httpProvider.defaults.headers.common["Authorization"] = token YOUR_TOKEN;
My REST backend [ based on NodeJS/express/mongojs] is complaining 404 (not found) when Params are attached as part of URL. Backend rest interface is coded as below;
var express = require('express');
var router = express.Router();
router.get('/login', auth.signin); //auth.signin is code to verify user
Above REST service is consumed by AngularJS based frontend through $resource as below;
Definition:
angular.module('myapp').factory('signinmgr', function($resource) {
return $resource("http://localhost:3000/login", {}, {
'get': {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}});
Usage:
signinmgr.get({'username':'myname', 'password':'mypass'}, function(data){
//success
}, function(x){
//failed
});
Problem:
Frontend code above produces a URL to consume REST service where parameters are part of URL i.e. http://localhost:port/login?username=myname&password=mypass [if I use GET method, POST is OK]. I wanted my front end to keep URL as http://localhost:port/login and post any parameters through body as backend is using req.body.paramName to read those. [Actual Solution]
If (1) cannot be done, and my frontend is sending params as part of URL, I needed help as to know how to equip my backend to allow this URL with parameters so that backend doesnt return 404 as the base URL http://localhost:port/login is already there.
PS: for (1), I tried this thread with data:{username:'',password:''} but of no use. Please help if I am missing something very obvious or some concept.
Try the $http service instead:
angular.module('myapp').factor('signinmgr', function($http) {
return {
login: function (username, password) {
$http.post("http://localhost:3000/login", {
username: username,
password: password
}
}
};
});
signinmgr.login('myname', 'mypass').then(function(data){
//success
}, function(x){
//failed
});
Each request that my nodejs/expressjs backend receives has three places for passed attributes;
params{}
query{}
body{}
My problem (1) cannot be fixed in case I want to use GET method since with GET request parameters are visible as part of URL i.e. http://localhost:port/login?username=myname&password=mypass. To send my username/password I had to use POST that sends parameters as part of body{}.
My problem (2) was that I was using GET and mistakenly looking for parameters in body{} of request. Instead, parameters passed as part of URL in GET request are added to query{} of the request.
I'm a bit of a noob with Angular and am having issues trying to post to a Drupal Services endpoint. I can post just fine with HttpRequester (FFox plugin), however all my attempts with Angular to post data to get a session result in 401 Unauthorized: missing required argument username or other errors.
Here is my testing factory resource with default input:
userInfoApp.factory('LoginService', function($resource) {
return $resource('/auth-service/user/login', {username: 'admin', password: 'admin'}, {
update: {
method: 'POST', // this method issues a POST request
headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}
}
});
});
Here is the call I am making to it inside the controller (this):
this.login = function() {
var login = LoginService.update(function(data) {
console.log(data);
});
};
So far this usually results in a query string generated like so:
http://project.loc/auth-service/user/login?password=admin&username=admin
and the response of:
401 Unauthorized : Missing required argument username
What might I be doing wrong here? I have gotten $resource to work just fine with other endpoints (like for a menu service to retrieve a menu) however posting seems to be much more finicky. Any suggestions would be appreciated.
Thanks
Your current configuration for $resource sends username & password as querystring. Hence they appear in your URL. I assume you need these values to be POST.
According to documentations all non GET methods have a payload parameter in the action method:
non-GET "class" actions: Resource.action([parameters], postData,
[success], [error])
What you need to do is stop sending the parameters as default [parameters] and make use of the postData to POST data to your Drupal Services endpoint. You could do this when you call $update() as:
LoginService.update({}, {username: "admin",password: "admin"});
Ok, I found a way that works; for the controller function:
this.login = function() {
var data = {
username: this.username,
password: this.password
};
var login = LoginService.save({}, data,
function(data) {
// possibly do stuff with result
},
function(reply) {
// Comments on best way to access $scope here are welcome.
// This feels a bit weird to me.
$scope.info.errorMessage = reply.statusText;
}
);
};
And for the factory service:
// Login factory service
userInfoApp.factory('LoginService', function($resource) {
return $resource('/auth-service/user/login');
});
This actually retrieves the session id and other user data from the server.
I have a WebAPI method here:
http://localhost:50463/api/movies
and when accessing it from a browser it loads perfectly.
In my project (the same project as where Web API resides) when calling the method from AngularJS I get an error 500:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
When I click the link in the error it loads the data perfectly.
The routing for WebAPI is as follows:
config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}",
new {action = "Get"},
new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)}
);
This is the angular call
app.factory('dataFactory', function ($http) {
var factory = {};
factory.data = function (callback) {
$http.get('/api/movies').success(callback);
};
return factory;
});
I added this javascript just to rule-out angular, I get the same:
$.ajax({
url: "/api/movies",
type: 'GET',
//data: "{ 'ID': " + id + "}",
contentType: "application/json; charset=utf-8",
success: function(data) {
alert(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(thrownError);
}
});
Any idea what I have done wrong?
I assume your Web API and AngularJS app are running on a different port. In this case you are running in a Same-Origin-Policy issue.
Ensure your Web API is responding with HTTP CORS headers e.g.:
Access-Control-Allow-Origin: http://<angular_domain>:<angular_port>
or
Access-Control-Allow-Origin: *
Doesn't look like a CORS issue to me as you are using a relative URL in your $.ajax() request.
Did you try $.getJSON("/api/movies").then(successHandler, faulureHandler)
Not sure of that will help but for one you are sending a contentType header with a GET request which contains no content. There should be an Accept header instead, but WebAPI should be fine without one.
I would also remove the constrains from the routing and revert back to the default route mapping to see if the problem is there.
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new {id = RouteParameter.Optional});