Ole Ole - 1 month ago 30
HTTP Question

Post Basic Authentication semantics with AngularJS and Spring Boot?

I'm reading through the following tutorial. The AngularJS navigation controller fires when the page loads, and that includes executing an authenticate function that looks like this:

var authenticate = function(credentials, callback) {

var headers = credentials ? {
authorization: "Basic " +
btoa(credentials.username + ":" + credentials.password)
} : {};

$http.get('user', {
headers: headers
}).then(function(response) {
if (response.data.name) {
$rootScope.authenticated = true;
} else {
$rootScope.authenticated = false;
}
callback && callback();
}, function() {
$rootScope.authenticated = false;
callback && callback();
});

}


The function is called like this:
authenticate()


So no credentials or call back is passed, so the authentication obviously does not succeed on page load. IIUC the reason this is called like this is in the event of a browser refresh post login. So my question is how does this function succeed in setting
$rootScope.authenticated
to true post login, since IIUC the basic authentication
headers
value sent with the ajax request (The line
$http.get('user', {headers : headers}
) will be
{}
. Will Spring simply let the request through since the user is already authenticated and does this happen because the
Authentication Basic
header is set post form authentication?

Answer

The point here is that Spring Boot (in reality Spring Security) creates an HttpSession on the backend side after successful authentication.

Actually, the backend is stateful because it retains session data (authenticated user) on the server side. A client (here browser) gets identified by the supplied session id.

The authentication flow looks like this from a protocol perspective.

Use case: Browser initiates an HTTP call to the backend: Backend answers with Unauthorized because the client tries to access a protected resource without Authorization header

$ curl -I -H "X-Requested-With:XMLHttpRequest" http://localhost:8080/user

HTTP/1.1 401
Set-Cookie: XSRF-TOKEN=b1137571-5e15-491c-8df5-9db5d34f29a8;path=/
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Transfer-Encoding: chunked

Use case: Preemptive basic authentication. Backend grants access to resource and notifies client about session id

$ curl -I -H "X-Requested-With:XMLHttpRequest" -H "Authorization:Basic dXNlcjpwYXNzd29yZA==" http://localhost:8080/user

HTTP/1.1 200
Set-Cookie: XSRF-TOKEN=ef72f0b8-4262-4ea2-8a46-5f7e19558079;path=/
Set-Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2;path=/;HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type    : application/json;charset=UTF-8
Content-Length: 343

Due to the fact that the browser sends valid user credentials via the HTTP header Authorization, the backend creates a new HttpSession and sends back an identifier (the session id) to the browser

Set-Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2;path=/;HttpOnly

Now, the browser attaches to every subsequent HTTP call the Cookie header automatically

Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2

which prevents a new authentication cycle. As long as the session does not timeout on the server side, the server "assigns" the session data to this particular HTTP request.

Basic authentication is not limited to Spring Boot (Spring Security) nor Angular. It is a standardized authentication scheme for the Internet since many years.

Bear in mind, basic authentication is an old-fashioned authentication scheme and highly discouraged in modern web applications.

Will Spring simply let the request through since the user is already authenticated and does this happen because the Authentication Basic header is set post form authentication?

Regarding the quoted example, you may now understand why an empty headers object does not lead to a rejection.

  1. The browser (the Angular application) sets preemptive the Authorization header with valid user credentials and initiates an HTTP request to a restricted resource
  2. The server validates user credentials, creates an HttpSesion identified by a session id and sends it back to the browser
  3. The browser attaches the session id as a cookie to every subsequent HTTP request automatically
  4. The server identifies the HTTP request is belonging to a particular session (authenticated user) stored on the server and accepts the incoming request

What happens if you instruct Spring Security not to create sessions on the server? For that purpose append

.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

to SecurityConfiguration::configure.

Now, you can see that every call to /resource in the example leads to an HTTP 401 because there is no session on the server anymore. The Angular application is now forced to set the Authorization header on every HTTP request on its own.

The browser may attach Authorization header automatically too. For the sake of brevity, I will skip that part. Please keep in mind, basic authentication is highly discouraged in modern web applications.

The intention of the example is not to show you how to implement a sign in with Spring Boot and Angular. The main intention is to evolve the primitive sign in towards a single sign on based on OAuth2 and Spring in a microservice architecture.

Don't take the code example for granted.

Comments