How to handle authentication in Angular JS application - session

I am implementing an auth system in my angular js app.
What I am planning it like below:
Get user info(name and pass from login form)
Check whether user exists or not
if exists server respond with a session cookie and frontend will redirect to a certain page.
then user will do some task which will generate API request
API request should have cookie information that was sent on step 3
server check whether the cookie was generated or not and if cookie was found then respond with the API request results. And in my service I am doing something like
MyApp.service('myAuth', function($http, $q) {
this.authHeader = null;
this.checkAuth = function(){
//do api call and if success sets this.authHeader = response
}
this.isAuthenticaed = function(){
this.authHeader ? return this.authHeder : return false;
}
After submitting the login form I will call checkAuth and get my session cookie back from my server, how I can add the cookie information while doing the next REST call and also when user will navigate throughout the application after log in I do want to check each time isAuthenticaed true or false, in Angularjs when it will navigate to another page does it resets after setting it true from the first call? And is my approach 1-6 good or do you have any specific suggestions?
Btw I checked previous so entries but those are not what I want to know.

I am not sure about your backend, but this is how I would do it
Create a separate login page (dedicated url not angular sub view or
modal dialog).
If the user is not authenticated redirect to this login
page. This is done by server redirects. This page may or may not use
angular framework, as it just involves sending a user\password to
server.
Make a POST (not AJAX request) from the login page, and verify on server.
On the server set the auth cookie. (Different frameworks do it differently. ASP.Net sets form authentication cookie.)
Once the user is authenticated redirect user to the actual angular app and load all its components.
This saves any code require to manage authentication on client side in Angular. If the user lands on this page he is authenticated and has the cookie.
Also default browser behavior is to send all cookies associated with a domain with each request, so you don't have to worry if angular is sending some cookie or not.

I use the http-auth-interceptor. http://ngmodules.org/modules/http-auth-interceptor
In my backend (asp.net mvc) I build a simple Authentication Service and return an http error 401 if the user is not authenticated.
Then I handle the error with a login-view in the SPA site.

The ideas put forth by the previous answers will work, but I think they're overkill. You don't need anything this complex.
how I can add the cookie information while doing the next REST call
Turn on withCredentials by default inside $httpProvider like so:
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}]);
Then remove the wildcard (if you had one) from the CORS-related headers, and set allow-credentials, on the server side. In my case, using Python + Flask + Flask-Restful, it's super easy and looks like this:
import Flask
from flask_restful import Api
app = Flask(__name__)
api = Api(app)
api.decorators = [cors.crossdomain(origin='http://localhost:8100', credentials=True)]
Now cookies will be set and returned automatically and transparently by the browser. See these threads for more info:
$http response Set-Cookie not accessible
Angularjs $http does not seem to understand "Set-Cookie" in the response
when user will navigate throughout the application after log in I do want to check each time isAuthenticaed true or false
As suggested above, have the server return 401 if the auth session expires or is deleted, and use $httpInterceptor in Angular to catch this like so:
app.config(function($httpProvider) {
var interceptor =
function($q, $rootScope) {
return {
'response': function(response) {
return response;
},
'responseError': function(rejection) {
if (rejection.status==401) {
// Modify this part to suit your needs.
// In my case I broadcast a message which is
// picked up elsewhere to show the login screen.
if (!rejection.config.url.endsWith('/login'))
{
$rootScope.$broadcast('auth:loginRequired');
}
}
return $q.reject(rejection)
}
}
};
$httpProvider.interceptors.push(interceptor);
});

(Disclosure: I'm one of the developers of UserApp)
You could use the third-party service UserApp for this, together with the AngularJS module.
Check out the getting started guide, or take the course on Codecademy. Here's some examples of how it works:
Login form with error handling:
<form ua-login ua-error="error-msg">
<input name="login" placeholder="Username"><br>
<input name="password" placeholder="Password" type="password"><br>
<button type="submit">Log in</button>
<p id="error-msg"></p>
</form>
User info is accessed using the user service: user.current.email
Or in the template: <span>{{ user.email }}</span>
Signup form with error handling:
<form ua-signup ua-error="error-msg">
<input name="first_name" placeholder="Your name"><br>
<input name="login" ua-is-email placeholder="Email"><br>
<input name="password" placeholder="Password" type="password"><br>
<button type="submit">Create account</button>
<p id="error-msg"></p>
</form>
ua-is-email means that the username is the same as the email.
How to specify which routes that should be public, and which route that is the login form:
$routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
$routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
The .otherwise() route should be set to where you want your users to be redirected after login. Example:
$routeProvider.otherwise({redirectTo: '/home'});
Log out link:
<a href="#" ua-logout>Log Out</a>
Hide elements that should only be visible when logged in:
<div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>
And to authenticate to your back-end services, just use user.token() to get the session token and send it with the AJAX request. At the back-end, use the UserApp API to check if the token is valid or not.
If you need any help, just let me know :)

Related

Mixed Content on Vue-select Component

this is a bit of a weird one.
On my local development environment, this works perfectly, but once I go on a staging or live server - things don't go as smooth.
I am using the https://vue-select.org/ component for Vue.js to pull through options based on user input. When the select textarea is changed, I debounce the method and fire the string off to an API and pull back any results that are relative to the user's input. This then gets populated into an 'options' array which the select dynamically updates and uses.
The API link is specified using a variable from my .env file. On my local environment using Laravel Valet, this works fine.
When the site gets switched over to a live server, things get interesting. When I try and input a value into the select field, I get this result:
Mixed Content: The page at 'https://example.com/cv/1fa2383/edit' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.com/api/job_title_names'. This request has been blocked; the content must be served over HTTPS.
Now, you would assume that this was because the request is being loaded over HTTP, not HTTPS. This is the weird part. Nowhere hardcoded, or defined as a variable is there HTTP requests, only HTTPS. I've quadruple tested this and re-written and even hard-coded HTTPS links but for some reason, the component keeps trying to load the request over HTTP.
It's really odd because it tries to fire two requests. One is a GET and the other is a POST. The POST is what my Axios script should be doing, but the GET request gets added for some unknown reason. There is no code built for a get request on this component as you will see below.
I'm at my witts end with this. Here's some information regarding my components and element references:
My env variables: (.test is my dev tld).
test is my dev tld
/api/ is what most of my axios requests go through.
APP_URL=http://example.test
APP_API_URL=http://example.test/api/
My input:
<label>
<span class="block font-bold mb-2 text-grey-500 text-sm">Job Title <i class="fad fa-question-circle leading-none text-blue-400" v-tooltip.top-center="'What was your role in this company?'"></i></span>
<v-select taggable :options="jobTitleOptions" #search="fetchJobTitleOptions" v-model="newEmployment.jobTitle" :filterable="false" class="dynamic-select" placeholder="Type a Job Title">
<template slot="no-options">
Type to find your job title...
</template>
</v-select>
</label>
My methods:
fetchJobTitleOptions (search, loading) {
loading(true);
this.searchJobTitles(loading, search, this);
},
searchJobTitles: _.debounce((loading, search, vm) => {
console.log('Job Title Url: ' + vm.jobTitleOptionsUrl);
if(search != '') {
axios.post(vm.jobTitleOptionsUrl, {
name: escape(search)
})
.then(response => {
let data = response.data;
vm.jobTitleOptions = data;
loading(false);
});
} else {
loading(false);
}
}, 500),
Computed:
jobTitleOptionsUrl: function() {
return this.url + 'job_title_names/'
},
My component reference:
apiurl is the value of APP_API_URL assigned to a different vue variable.
<edit-employment :apiurl="apiurl" :cvid="this.cv_id"></edit-employment>
Blade template:
#extends('layouts/app')
#section('content')
<edit-cv url="{{ env('APP_URL') }}" api="{{ env('APP_API_URL') }}" cvid="{{ $cv_id }}" urlslug="{{ $url_slug }}"></edit-cv>
#endsection
Using Vue tools, I can see that all the links are being referenced correctly, not one has HTTP prefixed before them.
I am running Nginx on my local environment, but Apache is running on my server. I'm not sure if that could help with some diagnosis?
Steps I've taken to try and solve this:
I have flushed all cache from the Laravel side of things successfully
I have returned the variables in Laravel and can confirm they return correctly.
I have re-written most variables to ensure that they're 100% correct
I have checked package versions to see if there are conflicts, of which there are not.
Any help would be greatly appreciated.
Thank you
I fixed this by double-checking my routes. When working with APIs and specifically Axios in Laravel, if you have a trailing / at the end of the route, this causes the request to 301. After removing the trailing slash, everything worked as it should.
So this route:
return this.url + 'job_title_names/'
Becomes this:
return this.url + 'job_title_names'

Create cross domain cookies for single sign on

I want to implement single sign on from my website(assume it as a.com) to vendor website(assume it as b.com)
My vendor is providing a service which will take user Id as input and returns token and cookies in response header. I need to call this service and redirect to vendor url with session token through post request and set cookies(which are received from service response).
In my code after making service call i am returning url and token to a jsp and cookies in httpservletresponse . Javascript in this jsp will autosubmit the form on page load to make post call. But when it is redirected, browser is not setting the b.com cookies in the request header.
Controller code :
#RequestMapping(value = "/sso", method = RequestMethod.GET)
public String ssoToVendor(final Model model, final HttpServletResponse response) {
/**
*Service call happens here and returns tok
*/
model.addAttribute("url","https:\\b.com");
model.addAttribute("tok",tok);
for (String cookie : cookies) {
response.addHeader("Set-Cookie", cookie);
}
return "dummyjsp"
}
JSP sample code :
<body>
<form id="redirect" action="${url}" name="redirect" method="POST">
<input type="hidden" name="tok" id="tok" value="${tok}"/>
</form>
</body>
<script type="text/javascript">
var redirect = document.getElementById("redirect");
redirect.submit();
</script>
I know that it is not possible to set cross domain cookies but some how there is another application which is implemented in c# is able to set those cookies.
Is there a way we can set b.com cookies in response header and that is created by browser and sent to b.com when redirected from a.com in java.

How can I set CSRF in a form in Appcelerator?

I am creating an app that has a login. I want that login to match the username and password but I cannot make it happen because I cannot send the "_token" field, and I cant because I cannot generate the same token that the application will wait for.
This is my current form:
<View id="loginView" layout="vertical">
<TextField id="inputUsername" />
<TextField id="inputPassword" passwordMask="true" />
<Button id="buttonLogin" onClick="performLogin" />
<ActivityIndicator id="activityIndicator" />
</View>
This is my current login request on Appcelerator.
loginReq.open("POST","http://192.168.100.29/miapp/mobile/auth/login");
var params = {
username: $.inputUsername.value,
password: $.inputPassword.value
};
loginReq.send(params);
And it should be something like:
loginReq.open("POST","http://192.168.100.29/miapp/mobile/auth/login");
var params = {
username: $.inputUsername.value,
password: $.inputPassword.value,
_token: $.inputTokenOrSomething.value
};
loginReq.send(params);
Also I created a route in the routes.php file in Laravel, as follows:
Route::get("mobile/auth/login", function(){
echo ("Debug"); exit;
});
Does anyone has an idea? Thank you.
Normally you want to send a CSRF Token via a Request Header. This is how it's done with Appcelerator when sending a token to Drupal:
loginReq.open("POST","URL");
loginReq.setRequestHeader('X-CSRF-Token',TOKENHERE);
loginReq.send(params);
Note, your Request Header goes AFTER open, but BEFORE send.
Edit:
Forgot to add - Normally you send a log in without a token, then capture the token from the servers response after a successful log in (usually sent as XML, JSON, etc... depending on the set up). This token is stored and sent in the request headers anytime you need to authenticate yourself to perform an action (like posting, deleting content). Again, this all depends on the set up.

Yammer javascript SDK logout issue

I can successfully login with yam.platform.login but when I call
yam.getLoginStatus(
function (response) {
if (response.authResponse) {
yam.platform.logout(function (response) {
if (response) {
alert("logout success");
}
})
}
}
);
the inner callback function is never reached. Do people know what is happening?
EDIT: another strange behavior that could be related to the problem: after the above logout function call, the login status is still "connected" and I checked in Chrome that all cookies from yammer.com are deleted. But when I manually ask Chrome to delete the cookies, login status would return "unconnected".
Response from yam.platform.logout seems "false" after successful logout so you might try "if(response == false)" or even without if statement..
I was also facing the same issue. It is hard to believe that the issue still exists in 2018! I dug a bit more and found that after the app is authorized by user, Yammer server sends a cookie which gets stored in the browser somewhere (not tied to the session) and yam.platform.logout is unable to delete this cookie (Ideally it should!)
But I found a good workaround which is working neatly for me.
Below is JS in my login page:
$("#yammer-login").click(function(){
console.log("clicked");
yam.getLoginStatus(
function(resp) {
yam.platform.login(function (response) { //prompt user to login and authorize your app, as necessary
if (response.authResponse) {
console.dir(response); //print user information to the console
alert("login success");
}
$.ajax({
type:"POST",
url:"/setSession",
data:JSON.stringify(response,null,'\t'),
contentType:"application/json;charset=UTF-8",
success: function(result){
alert("Result from setSession is: "+result);
window.location.replace("/login");
}});
});
}
);
});
Here #yammer-login is the id for login element
<h2 class="sign-in">
<center>
Sign-in With Yammer
<br><br>
<span id="yammer-login">Click</span>
</center>
</h2>
Here is my workflow:
The JS on login page sends a POST request to setSession and sets the session. The execution of window.location.replace("/login"); sends a GET request to my server for /login url. As the session is now set, my server then redirects this request to the dashboard. After I click on logout button on the dashboard. I clear all the session cookies and redirect it back to the login page. As the session is now un-set- I see the login page again! All works smooth!
So, the next time user clicks on #yammer-login DOM element - the session gets set and she gets redirected to dashboard (this time without authorizing the app)!
Hope this helps someone who faces this issue like me in the future!

Django - Start Session by Ajax Request

I need to know how to start a session by Ajax in Django. I'm doing exactly as described bellow, but it is not working! The request is sent correctly, but don't start any session. If a request directly without ajax it works! What is going on?
'# urls
r'^logout/$', 'autenticacao.views.logout_view'
'# view of login
def login_view(request):
username = request.GET.get('username', '')
password = request.GET.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponse(user.get_profile().sos_user.name)
return HttpResponse('user invalido')
'# ajax in a html page
$(function(){
$.get('http://localhost:8000/logout/?username=usuario?>&password=senha', function(data){
alert(data);
});
You're not calling the login_view. You're ajax request is going to the /logout/ url which is calling the autenticacao.views.logout_view.
Also, The ?> after username=usuario doesn't look right in the your get url.
My guess is you should be doing something like http://localhost:8000/login/?username=usuario&password=senha. (but I'd need to see your login url mapping to be sure).
Also, you should be POSTing the login information and using HTTPS for security reasons, but that's a different issue.

Resources