Restangular POST fails https:// OPTIONS pass - javascript

I was able to convert most of my existing services to use Restangular. Everything apart from POST is working properly.
Original POST service that works
app.service('APIService', function($http, $q){
...
this.post = function(api, route, params){
var d = $q.defer();
$http({
url : base_urls[api] + route,
method : 'POST',
data : params,
withCredentials: true,
useXDomain : true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).success(function(data){
d.resolve(data);
try{
toastr.success(toastMsg[route].win, 'Success');
} catch(err){}
}).error(function(data){
d.reject(data);
try{
toastr.error(toastMsg[route].fail, 'Whoops');
} catch(err){}
});
return d.promise;
}
});
Which is used like this:
app.controller('AuthController', function($scope, APIService){
$scope.login = function(username_or_email, password, redirectUrl){
APIService.post('root', '/login', {
'username_or_email' : username_or_email,
'password' : password
}).then(function(r){
if(r.success){
window.location = redirectUrl;
}else
{
// handle this
}
});
};
});
Conversion to Restangular
app.controller('AuthController', function ($scope, toastrFactory, Restangular) {
$scope.login = function (username_or_email, password, redirectUrl) {
var login = Restangular.one('auth'),
creds = {
'username_or_email': username_or_email,
'password': password
};
login.post('login', creds).then(function (r) {
window.location = redirectUrl || '/profile';
}, function () {
toastrFactory.error(['Error', 'Login not successful'])
})
};
});
The above fails the pre-flight OPTIONS pass, and gives up. What is the difference between my original service and the Restangular call I'm trying to use?
Worth noting I did set default config params for Restangular (to mirror the original service)
RestangularProvider.setBaseUrl('https://dev.foo.com/');
RestangularProvider.setDefaultHttpFields({
withCredentials: true,
useXDomain : true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
What's strange is my Restangular GETs that require credentials on https:// WORK, and successfully pass the OPTIONS phase, and manage to send cookie data.
Any help is appreciated. Thanks.

I'm not sure about the actual route you are trying to reach, but the Restangular.one('auth') seems like you'd need to define the resource identifier (eg. POST /auth/123?username_or_email=moi).
If you're trying to reach POST /auth?username_or_email=moi (with your cred in the HTTP parameters), then try Restangular.all('auth').
If this didn't solve the problem, please provide the URI you're seeing in the browser's network inspector along with the URI you'd want to reach.

Related

$cookies not working in angularjs

I want to keep user login status even refresh the browser. But I find a problem when I apply $cookies in angularjs. Following is my code:
function signIn() {
var params = {email: vm.email, password: vm.password};
Service.authBuffer = _.extend(Service.authBuffer, params);
$.ajax({
type: 'POST',
url: GetEndpointUrl('/api/auth/login'),
dataType: "jsonp",
data: params,
success: function(data) {
console.log("login data", data);
$scope.safeApply(function() {
Service.authBuffer = _.extend(Service.authBuffer, data);
console.log("login auth data", Service.authBuffer);
$location.path('/task');
var cookieExp = new Date();
cookieExp.setDate(cookieExp.getDate() + 7);
$cookies.put('Service.authBuffer', Service.authBuffer.email, { expires: cookieExp });
})
}
})
}
So I am wondering how to change the code to make $cookies work.
You probably have not injected the ngCookies module. Use something like this
angular.module('cookiesExample', ['ngCookies'])
.controller('ExampleController', ['$cookies', function($cookies) {
// Retrieving a cookie
var favoriteCookie = $cookies.get('myFavorite');
// Setting a cookie
$cookies.put('myFavorite', 'oatmeal');
}]);
Reference Link
Answer few of this questions to get better understanding of the problem:
- please provide the error message you get in console log of browser
- have you included angular-cookies library?
- have you injected ngCookies in app module?
- have you injected $cookies in controller?

AngularJS - Dynamic URL with ID

config.js
angular.module('config', []).constant('ENV',
{
name: 'My Angular Project',
apiEndPoint: 'http://SOMEIP/myServer', //API host,
adminUrl:'/admin/regionid/site/siteid/admin/regionid', //endpoint
loginUrl:'/login/regionid/site/siteid/device'
});
controller.js
this.userLogin = function(username, password) {
var adminServicePath = ENV.apiEndPoint + ENV.adminUrl
//final url = http://SOMEIP/myServer/admin/1/site/1/admin/1
var loginServicePath = ENV.apiEndPoint + ENV.loginUrl
//final url = http://SOMEIP/myServer/login/2/site/2/device
return $http({
method: 'POST',
url: adminServicePath,
headers: {
"Authorization": "Basic ",
"Content-Type": "application/x-www-form-urlencoded"
}
})
};
Here I am appending API with endpoint to form a complete URL. My issue is regiondid and siteid are dynamic. After user logs in, one REST API request will fetch siteid and regionid in response.
How do I dynamically replace siteid and regionid in URL with ID's
received in API response? After receiving id's in response, call a function that replaces the value.
You can use the String.prototype.replace(substr, newsubstr)
You can keep regionID instead of ?
var ENV = {
name: 'My Angular Project',
apiEndPoint: 'http://SOMEIP/myServer', //API host,
adminUrl: '/admin/?/site/?/admin/?', //endpoint
loginUrl: '/login/?/site/?/device'
};
var adminServicePath = ENV.apiEndPoint + ENV.adminUrl.replace("?", 1).replace("?", 1).replace("?", 1);
console.log("Final url admin : " + adminServicePath);
var loginServicePath = ENV.apiEndPoint + ENV.loginUrl.replace("?", 2).replace("?", 2);
console.log("Final url login : " + loginServicePath);
instead of constant, you can use value.
angular.module('config', []).value('ENV',
{
name: 'My Angular Project',
apiEndPoint: '', //API host,
adminUrl:'', //endpoint
loginUrl:''
});
Inject ENV and set all values after API call.
ENV.name = xyz;
ENV.apiEndPoint = xyz;
ENV.adminUrl = xyz;
ENV.loginUrl = xyz;
but the values might get set to default once you refresh the browser.
I'll assume that the siteid and the regionid can only be obtained from the response to the login endpoint.
Using a constant might not be the best idea here for obvious reasons (i.e. they're constant, and can't be created at the time you want to create them).
Instead, you could do one of a few things - a simple solution that probably works for a lot of use cases would be to create a login service that wraps your API call and then sets a value either in the service or another service that can be injected into wherever you need it.
It might look like this:
angular.module('app')
.service('loginService', function($http) {
var siteId,
regionId;
function login(username, password) {
return $http({
method: 'POST',
url: '<login endpoint here>',
headers: {
"Authorization": "Basic ",
"Content-Type": "application/x-www-form-urlencoded"
}
})
.then(function(result) {
siteId = result.siteId;
regionId = result.regionId;
});
}
);
This makes the values available to you any time you need to make an API call after logging in. However, this isn't great since you will need to inject the loginService into any controller/service that needs it, and that controller/service might not really care about the login service at all.
An improved approach to this could be to have an API service that performs the http gets/sets/puts/posts/whatever and that is accessed by your data access layer. Inside this service, you can set/get the siteid and regionid.
It might look like this:
angular.module('app')
.service('api', function($http) {
var siteId,
regionId;
var defaultHeaders = {
"Authorization": "Basic ",
"Content-Type": "application/x-www-form-urlencoded"
};
function post(url, options) {
return $http({
method: 'POST',
url: url,
headers: options.headers ? options.headers : defaultHeaders
});
}
// Other functions to perform HTTP verbs...
});
angular.module('app')
.service('loginService', function(api) {
function login(username, password) {
api.post('urlHere', options)
.then(function(result) {
api.siteId = result.siteId;
api.regionId = result.siteId;
});
}
});
You can then access the siteid and regionid where you like.
For example:
angular.module('app')
.controller('someService', function(api) {
function doSomethingWithTheApi() {
var url = 'www.google.com/' + api.siteId + '/' + api.regionId + 'whatever-else';
return api.post(url, {});
}
);
Note: the code above isn't complete, but it gives you a very good idea of the approach you could take that is reasonably clean, not too hacky and is easily testable :)
Hope that helps!

AngularJS "localStorage.getItem" is null in $resource

I have an angular $resource for login and getting user's info. The login sends the username and password to server and gets a Bearer token. In the success function of User.login the token is stored in localStorage. In getEmail, I'm including the token in header to the user's email address.
The problem is, the first time getEmail is called in controller, $window.localStorage.getItem('TK') evaluates to null but if I refresh the page then it works correctly. All of the subsequent requests to the server that have the token included work as expected. What could cause this?
Not to mention, getEmail is called way after login and I have confirmed that the token is present in localStorage before the getEmail is called.
angular.module('authentication')
.factory('User', function ($resource, $window) {
return $resource('/login', {}, {
login: {
method: 'POST'
},
getEmail: {
url: '/user',
method: 'GET',
headers: {
'Authorization': "Bearer " + $window.localStorage.getItem('TK')
}
}
});
})
In response to # comment:
My controller looks like this:
angular
.module('app.mymodule', ['ngReource', 'authentication')
.controller('myCtrl', mymodule);
mymodule.$inject = ['User', '$window'];
function mymodule(User, $window) {
// THIS PRINTS THE TK IN CONSOLE CORRECTLY
console.log($window.localStorage.getItem("CHTOKEN"));
// THIS IS UNAUTHORIZED CUZ TK IS NULL
User.getEmail({}, function(data){
console.log('set the email');
});
}
Your problem is that the resource definition is provided at the time of creation (before you have saved a token).
You can either add a general request interceptor to the $httpProvider so all requests are covered or change your getEmail action's header to use a function so it is evaluated at run time, eg
headers: {
'Authorization': function() {
var token = $window.localStorage.getItem('TK');
return token ? 'Bearer ' + token : null;
}
}

How can I use"$timeout" in factory using AngularJS?

I have the services and within particular time duration if response is come than ok, other wise show error in popup.
Here is my service code:
angular.module('server', [])
.factory('api', function($http) {
var server = "http://myapi-nethealth.azurewebsites.net";
return {
//Login
login : function(formdata) {
return $http({
method: 'POST',
url: server + '/Users/Login',
data: $.param(formdata),
headers: { 'Content-Type' : 'application/x-www-form-urlencoded'},
})
},
};
});
Please tell me how can I use timeout property in services.
Read this post - How to set a global http timeout in AngularJs
Where you can set a timeout for your http calls.
You can inject the above factory in your controller and then make a call with success and error callbacks like below
api.login(formdata)
.success(function(){ alert("success"); })
.error(function(){ alert("error"); });

how to pass credentials with angular-http-auth?

Inside my app, authentication is working with angular-http-auth
when user fill the username / password form and submit, it targets the login() function from the controller :
$scope.login = function() {
var credentials = Base64.encode($scope.username + ':' + $scope.password);
$http.defaults.headers.common['Authorization'] = 'Basic ' + credentials;
User.login( // reference to the $resource service
function() { // success callback
authService.loginConfirmed(); // method of angular-http-auth
}
);
}
User is a service created with $resource
factory('User', function($resource){
return $resource('url/to/json',
{},
{ 'login': { method: 'GET', isArray:true }
});
})
and base64 is an encrypting service coming from here
This is working like this, but is there a way to pass the credentials through angular-http-auth instead of setting default headers through $http ?
The problem with doing this:
$http.defaults.headers.common['Authorization'] = 'Basic ' + credentials;
is that you're now setting the "credentials" for all $http requests that are made. If you don't want to do that, and you want to pass the "credentials" with each $resource call directly, you can, which is what it looks like you're starting to do there.
return $resource('url/to/json',
{},
{ 'login': {
method: 'GET',
headers: { 'Authorization': 'Basic ' + credentials }
}
});
I didn't try this very example, but I have I similar code where the method is a POST so I call save() on the result of $resource before returning it, and the 'login' key is 'save'
But since you're doing a GET, with a custom "login" method, this should work.

Categories