Akka http comes with support for basic authentication. This allows us to wrap the response in a basic authorization directive which will return a 401 Authorization Required header if the submitted credentials are not correct; if the authorization is correct it will return the wrapped route. The browser then will display a password challenge to send the credentials

When we do ajax (thick Javascript client) programming, we usually want to control the process so that the dialog box won’t be shown and so that we can handle prompting the user for the password, etc. This is all possible with Akka HTTP. This is how we would do it:

On the client:

On the way in, Basic Authentications expects  an ‘Authorization: Basic username:password’ header. 

handleLogin(ev) {
    ev.preventDefault();

    const data = new FormData();
    const auth = btoa('joe' + ':' + 'JoesPassword')
    const self = this

    $.ajax({
        url: '/do-login',
        data: data,
        contentType: false,
        processData: false,
        type: 'POST',
        beforeSend: function(xhr){
            xhr.setRequestHeader("Authorization", "Basic " + auth)
        },
        success: function(xhr){
            window.location = "/your-landing-page"
        },
        error: function(jqXHR, textStatus, errorThrown){
            self.setState({
                errorMessage:
                    <div className="alert alert-danger" role="alert">
                        Bad credentials
                    </div>
                })
        }
    });
}

Then we just need to put this markup snippet somewhere on our login page to show the error when there is one:

<div>
    <h1>Login</h1>
    {this.state.errorMessage}
    <form onSubmit={this.handleLogin}>
        Your login form
    </form>
</div>

On the server

Create a route like this based on the Akka HTTP documentation:


  authenticateBasic(realm = "secure site", myAuthenticator) { u =>
      .. log user in here ..
      log.info(s"login-succeeded: userId=$userId")
      complete(HttpResponse())
    }
  }
..
def myAuthenticator(cred: Credentials): Option[String] =
  cred match {
    case p @ Credentials.Provided(id) 
         if p.verify("p4ssw0rd") => Some(id)
    case _ => None
  }

This will return what’s inside, which is a status ok 200 in this example.

But if the auth is wrong, we get a 401 Unauthorized,  and the browser will show the auth required dialog box, which we want to avoid,  to make the user enter his credentials. So one thing we could do is convert this 401, to  a more generic error, so that the dialog won’t be triggered, but because it is still an error, we could catch it on the client side and handle it gracefully ourselves. A 401 Bad Request will do. Akka HTTP has a DSL to rewrite rejections from directives as follows:

val rejectionHandler = RejectionHandler.newBuilder()
  .handle{ case x => complete((StatusCodes.BadRequest, "Bad credentials")) }
  .result()

handleRejections(rejectionHandler){
  authenticateBasic(realm = "secure site", myAuthenticator) { u =>
      .. log user in here ..
      log.info(s"login-succeeded: userId=$userId")
      complete(HttpResponse())
    }
  }
}

Now our Javascript error handler function will get called, instead of being ignored, and the very unattractive browser password challenge dialog box won’t get displayed.

Mission accomplished!