Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to proper authenticate an AngularJS client to the server

I'm building an AngularJS web-app that uses a RESTful API (Jersey).

On the server side I am using a Java Application Server (in detail Glassfish 4).

My setup is as follows:

  • AngularJS webapp is deployed as a single war file to the Java EE Application server.
  • The RESTfulAPI (and the backend logic) is also deployed as a war file to the same Java EE Application server.
  • The AngularJS webapp calls the REST API to get the data it needs.
  • The RESTful API:
    • /api/public/* for public not restricted access (e.g. /api/public/users/signup to register as a new user). Any user is allowed to access this area. No login required
    • /api/private/* for restricted access (e.g. /api/private/account/{id} to retrieve some account specific data)
  • The private resources are protected with the Java EE internal security concepts (see below for web.xml details).

Web.xml

<security-constraint>
    <web-resource-collection>
        <web-resource-name>PRIVATE REST API</web-resource-name>
        <url-pattern>/private/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
        <http-method>HEAD</http-method>
        <http-method>PUT</http-method>
        <http-method>OPTIONS</http-method>
        <http-method>TRACE</http-method>
        <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
        <description>Have to be a USER</description>
        <role-name>USERS</role-name>
    </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>userauth</realm-name>
</login-config>
<security-role>
    <description/>
    <role-name>USERS</role-name>
</security-role>

This setup works for me, but only if I invoke the URLs manually in the browser. If I am not authenticated and address a secured area, the browser asks for username and password. If the provided credentials are valid, I from there on have access to the restricted area: Fine.

But how do I get that working with AngularJS? To login there is a POST API call: /api/public/users/login where you have to provide credentials from a form (username and password).

The client side code is:

ctrls.controller('LoginController', function($scope, $http, $log, $location, UserService, RemoteServerHelperService) {
    $scope.loginDataWrong = undefined;
    $scope.login = function(credentials) {
    $http.post(RemoteServerHelperService.buildURL('/public/users/login'), credentials)
        .success(function (data, status) {
            UserService.setLoggedIn(credentials.email);
            $scope.loginDataWrong = undefined;
            $location.path('/app');
            $log.info("login attempt: successfull. User.loggedIn:" + UserService.isLoggedIn());
        }).error(function (data, status, headers, config) {
            UserService.invalidate();
            $scope.loginDataWrong = true;
            $log.info("login attempt: failed. User.loggedIn:" + UserService.isLoggedIn());
        });
    };
});

The client side code seems to work. I also have some routes in place to secure content on the client side, and so on. There are several posts out there which describe the client side code in detail. So this "snippet" should be enough.

The server side code is:

@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
public Response login(Credentials credentials, @Context HttpServletRequest request) {
    if (isAlreadyAuthenticated(request)) {
        try {
            request.logout();
        } catch (ServletException ex) {
            return Response.status(Status.CONFLICT).build();
        }
    }

    // not authenticated
    try {
        request.login(credentials.getEmail(), credentials.getPassword());
        return Response.ok().build();
    } catch (ServletException ex) {
        return Response.status(Status.CONFLICT).build();
    }
}

But however, this does not "authenticate" the client side for further requests. When I make a API call on a restricted area after successfully login I get a 403 FORBIDDEN response from the server side. So I assume that I am doing something wrong. Do you have any ideas how to authenticate the client to the server for further requests?

A possible solution could be to switch to FORM-based authentication and simply invoke j_security_check with j_username and j_password, but for now I want to stick with BASIC-Authentication and perform the authentication manually via the RESTful API.

The setting $httpProvider.defaults.withCredentials = true; somehow doesn't seem to have any effect.

Update:

Thanks to the tip of lossleader I solved the problem. I removed the login() method, because I want the Java EE-Server to take care of authentication.

To access secured areas the AngularJS webapp has to set the HTTP-Header correctly. In my case $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>'; where username:password has to be Base64 encoded.

After I set the headers correctly, no Password Prompt is shown and the AngularJS webapp can access the REST API.

like image 276
zip Avatar asked Aug 18 '13 14:08

zip


People also ask

How to do authentication in AngularJS?

With Auth0, your AngularJS app only needs to talk to our API when the user logs in. All other API calls go directly to your server as they normally would. Using either our Lock Widget or your own custom login screen, your users send their credentials to our API to be authenticated.

How authentication work in Angular?

Your Angular application authenticates the user and receives an access token from Auth0. The application can then pass that access token to your API as a credential. In turn, your API can use Auth0 libraries to verify the access token it receives from the calling application and issue a response with the desired data.

How to secure API key in AngularJS?

If you are looking to architect secure authentication/authorization into your app, you will want to return a JWT to the AngularJS application. You can then pass this JWT as a Bearer token to your API which will verify the validity of the token and allow the API to authorize access to the AngularJS application.

Can we use basic HTTP auth in AngularJS?

AngularJS Authentication ServiceThe Authentication Service is the interface between the angularjs app and the server side api, so http requests are managed here. It also contains a Base64 service for encoding the username and password to be used in the HTTP Authorization header for all requests after logging in.


1 Answers

Thanks to the tip of lossleader I solved the problem. I removed the login() method, because I want the Java EE-Server to take care of authentication.

To access secured areas the AngularJS webapp has to set the HTTP-Header correctly. In my case $http.defaults.headers.common['Authorization'] = 'Basic '; where username:password has to be Base64 encoded.

After I set the headers correctly, no Password Prompt is shown and the AngularJS webapp can access the REST API.

like image 194
zip Avatar answered Oct 09 '22 17:10

zip