angular lib that wraps the excellent 'http-auth-interceptor' library to provide a config based login. Angular-login also provides a pluggable 'token manager' implementation (with a Grails Spring Security REST impl) so that getting more backend session impls online is easy to do
Download the production version or the development version.
angular-login.js
script provided by this component into your app's webpage. as well as the necessary dependant librariesIn your web page:
<script src="../../bower_components/angular/angular.js"></script>
<script src="../../bower_components/angular-cookies/angular-cookies.js"></script>
<script src="../../bower_components/angular-logging/dist/angular-logging.min.js"></script>
<script src="../../bower_components/lodash/dist/lodash.compat.js"></script>
<script src="../../bower_components/angular-resource/angular-resource.js"></script>
<script src="../../bower_components/angular-http-auth/src/http-auth-interceptor.js"></script>
<!-- endbower -->
<!-- endbuild -->
<!-- build:js scripts/app.min.js -->
<!-- to use the non-minified versions -->
<script src="../authManager/authManager_grailsSpringSecurityRest.js"></script>
<script src="../angular-login.js"></script>
<!-- to use the minified version (the auth managers have been concatenated with the main file -->
<script src="../<path>/angular-login.min.js
this project depends on lodash or underscore, angular-logging, angular, angular-resource, angular-cookies, and http-auth-interceptor .js libraries
currently there is only 1 token manager (works with grails spring security rest plugin) - include that or your custom authManager before including angular-login-service.js
// run blocks
myApp.run(['$rootScope', '$cookieStore', '$location', '$cookies', 'avLogin',
function($rootScope, $cookieStore, $location, $cookies, avLogin) {
/* Try getting valid user from cookie or go to login page */
avLogin.checkRequest($location, $cookieStore, $cookies, $rootScope);
}
]);
angular.module('myApp').controller('testController', ['$scope', 'avLog', '$rootScope', 'avLogin', '$resource', '$http', '$cookieStore', '$location',
function($scope, avLog, $rootScope, avLogin, $resource, $http, $cookieStore, $location) {
$scope.login = function() {
avLogin.login($scope.formData.name, $scope.formData.password, $http, $rootScope, $cookieStore, $location);
};
}
<form ng-cloak class="form-signin ng-cloak" role="form" ng-submit="login()">
<h2 ng-cloak class="form-signin-heading">Please sign in</h2>
<input ng-cloak type="text" name='j_username' id='username' class="form-control" placeholder="User Name" required autofocus ng-model="formData.name">
<input ng-cloak type="password" name='j_password' id='password' class="form-control" placeholder="Password" required ng-model="formData.password">
<button ng-cloak class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
If your login function returns a user - you can receive the resolved user as an event
$rootScope.$on('event:auth-loginConfirmed', function(event, user) {
logger.info("event:auth-loginConfirmed got session login event ", event);
logger.info("user data", user);
/etc....
});
$scope.logout = function() {
avLogin.logout($http, $cookieStore, $rootScope);
};
If you have race conditions - you can do any 'clearing' logic after the logout has been processed, by an event
$rootScope.$on('event:auth-logoutConfirmed', function(event, user) {
logger.info("event:auth-logoutConfirmed got session logout event ", event);
$scope.userInfo = {};
$scope.loggedIn = false;
});
Events are broadcasted on $rootScope
| Name | params | Description | | event:auth-loginConfirmed | event - TODO, user - the user returned from the remote login call | called on login | | event:auth-logoutConfirmed | event - TODO | called on logout | | event:auth-logoutFailed | rejection - any data about the failure that we have | called when the login fails (bad username, pass, etc..) | | event:auth-loginRequired | event | called when the interceptor detects that login is needed, if you specify 'redirectIfTokenNotFound' and a url then you don't need this event |
every api request to the service will call the auth manager to set the appropriate auth headers on the request to the server. Also, any cookies/session info in the response can be saved locally by the auth manager - this guarantees session handling and continuity
var AVaughanLoginConfig = {
/**
* how do we know what calls to intercept? anything with this in it will
* be intercepted and checked for login status and security tokens will
* be placed on the request by the auth manager
*/
restCallsWillContain: 'api',
/**
* where to post login form data
*/
loginUrlForRemote: '/api/login',
/**
* what to post the username under to the remote login url
*/
loginUserLabel: 'username',
/**
* what to post the password under to the remote login rul
*/
loginPassLabel: 'password',
/**
* where to post for logout on remote server
*/
logoutUrlForRemote: '/api/logout',
/**
* if the authentication token is not found - should we auto redirect?
*/
redirectIfTokenNotFound: false,
/**
* if the authentication token is not found - and we should auto redirect, what url should we go to for login?
*/
redirectIfTokenNotFoundUrl: '/login',
/**
* do we redirect after login?
*/
redirectAfterLogin: false,
/**
* where do we go after login if we should redirect after login?
*/
defaultUrlAfterLogin: '/',
/**
* do we do form encoding or json object posting?
*/
postType: 'JSON', //alternate is FORM
};
To override these settings
/**
* configure the login infrastructure
*/
myApp.config(function(avLoginProvider) {
//initialize and override 'someProperty' as well as choose with AuthManager to use (should handle
//whatever backend tokens are sent by the server session provider
var loginConfig = AVaughanLoginConfigFactory.create({
someProperty: 'new value'
}, GrailsSpringSecurityRestAuthManager);
//initilize login with our config...
avLoginProvider.initialize(loginConfig);
});
See getting started See also tests in this github project See example in source code
var loginConfig = AVaughanLoginConfigFactory.create({
someProperty: 'new value',
postType: 'FORM'
}, SpringSecurityAuthManager);
you will need to do the following (this example in grails)
/**
* Filter that writes the session token to a header
* the correct fix is to modify the system to not set the httpOnly header on the JSESSIONID - our version
* of grails does not honor the servlet spec setting and config
*
* User: aronvaughan
* Date: 9/4/14
* To change this template use File | Settings | File Templates.
*/
class SessionTokenFilter extends GenericFilterBean {
private static final Logger log = Logger.getLogger(SessionTokenFilter.class);
public final static String HEADER_NAME = "X-authtoken"
@Override
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = request
log.debug "Looking for session in request...${servletRequest.cookies}"
String tokenValue
servletRequest.cookies.each {
log.debug "${it.name} == ${it.value} "
if (it.name.equals("JSESSIONID")) {
log.debug(" found JSESSIONID ${it.value}")
tokenValue = it.value
}
}
if (tokenValue) {
log.debug "Token found: ${tokenValue}"
HttpServletResponse servletResponse = response
def c = new Cookie(HEADER_NAME, tokenValue)
c.maxAge = SystemSettings.getCurrentSettings().getSessionTimeout() * 60;
servletResponse.addCookie(c)
servletResponse.addHeader(HEADER_NAME, tokenValue)
} else {
log.debug "Token not found"
}
chain.doFilter(request, response)
}
}
sessionTokenFilter(SessionTokenFilter) {
}
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter 'sessionTokenFilter', SecurityFilterPosition.ANONYMOUS_FILTER.order + 1
}
Add toLogin($location) {} method to allow callers to force a login screen call - redirectIfTokenNotFoundUrl must be defined
add getHeader() method to auth manager (can be used to also configure non angular calls - i.e. dropzone headers)
allow json token name to be specified for spring rest security - default to 'access_token' to match latest grails side release if you aren't getting the cookie value set, look here (and check the login response from the service, the json return value token name should match)
auth managers expose isTokenValid method - if you are using grails REST spring security will call the validate token endpoint (requires, 1.4.0.RC5+ of the grails rest spring security plugin). If the token is invlid it removes it from the browser (this allows turning on anonymous access - if you send an invalid token the backend will send a 403 regardless if the resources is setp to allow access to all)
because of this checkRequest now takes a $http parameter
// run blocks
myApp.run(['$rootScope', '$cookieStore', '$location', '$cookies', 'avLogin', '$http',
function($rootScope, $cookieStore, $location, $cookies, avLogin, $http) {
/* Try getting valid user from cookie or go to login page */
avLogin.checkRequest($location, $cookieStore, $cookies, $rootScope, $http);
}
]);
fix handling of query params when forwarding to originalUrl after login
the provided token manager
the angular-http-auth library that this code is built upon
initial grunt workspace generated by angular-component
download the full source....
to test
to see the example app