-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication Authorization
Many web services need to apply some authentication and maybe authorization logic to some or even all parts of their
API. spray supports this with some dedicated directives: authenticate and authorize.
In order to be able to use them effectively one should understand the difference between 'authentication' and
'authorization'.
Authentication is the process of establishing a known identity for the user, whereby 'identity' is defined in the context of the application. This may be done with a username/password combination, a cookie, a pre-defined IP or some other mechanism. After authentication the system believes that it knows who the user is.
Authorization is the process of determining, whether a given user is allowed access to a given resource or not. In most cases, in order to be able to authorize a user (i.e. allow access to some part of the system) the users identity must already have been established, i.e. he/she must have been authenticated. Without prior authentication the authorization would have to be very crude, e.g. "allow access for all users" or "allow access for noone". Only after authentication will it be possible to, e.g., "allow access to the statistics resource for admins, but not for regular members".
Authentication and authorization may happen at the same time, e.g. when everyone who can properly be authenticated is also allowed access (which is often a very simple and somewhat implicit authorization logic). In other cases the system might have one mechanism for authentication (e.g. establishing user identity via an LDAP lookup) and another one for authorization (e.g. a database lookup for retrieving user access rights).
The authenticate takes one argument and extracts one value. Its argument is a GeneralAuthenticator which is defined
like this:
type GeneralAuthenticator[U] = RequestContext => Future[Authentication[U]]
type Authentication[U] = Either[Rejection, U]The GeneralAuthenticator does the actual job of either establishing a user identity (represented by an instance of U)
or providing a Rejection instance. The only implementation currently predefined in spray is the HttpAuthenticator,
which specializes the GeneralAuthenticator in that it uses the HTTP Authorization header to authenticate the user
and extract a user object. A further specialization, the BasicHttpAuthenticator implements HTTP Basic Auth. spray
comes with a convenience method called httpBasic, which simplifies the creation of a BasicHttpAuthenticator.
It takes two arguments:
- A String used for the authentication 'realm'
- A
UserPassAuthenticatorinstance, which is defined like this
type UserPassAuthenticator[U] = Option[(String, String)] => Future[Option[U]]and is responsible for creating (or not creating) a user instance given a username/password combination
The implementation of the httpBasic methods is this:
def httpBasic[U](realm: String = "Secured Resource",
authenticator: UserPassAuthenticator[U] = FromConfigUserPassAuthenticator)
: BasicHttpAuthenticator[U] =
new BasicHttpAuthenticator[U](realm, authenticator)If you don't specify your own UserPassAuthenticator implementation the FromConfigUserPassAuthenticator will be
used, which looks up usernames and passwords stored in plaintext in the spray configuration (see Configuration for
more on this).
You can use the authenticate directive like this:
authenticate(httpBasic(realm = "myResource")) { user =>
...
}Only requests with proper HTTP Authorization header containing an existing user/pass combination are passed through
to the inner route. If no Authorization header is present an AuthenticationRequiredRejection is created, which
results in the generation of a proper WWW-Authenticate challenge response header. If an Authorization header is
present but the user could not be authenticated the request is rejected with an AuthenticationFailedRejection
triggering a 403 Forbidden reponse by default.
The authorize directive comes in two flavors:
authorize(check: => Boolean)authorize(check: RequestContext => Boolean)
Both variants take a function determining whether the request is authorized. If it is the request is passed on to the
inner route unchanged, otherwise an AuthorizationFailedRejection is created, triggering a 403 Forbidden reponse
(the same as in the case of an AuthenticationFailedRejection) by default.
The authorize directive could for example be used like this:
path("somePath") {
authenticate(httpBasic(authenticator = MyAuthenticator)) { user =>
get {
authorize(user.hasReadingAccess) {
...
}
} ~
put {
authorize(user.hasWritingAccess) {
...
}
}
}
}This example uses a custom UserPassAuthenticator which created custom user objects supporting things like
hasReadingAccess and hasWritingAccess.
As an alternative to the FromConfigUserPassAuthenticator spray also comes with the LdapAuthenticator. This
UserPassAuthenticator implements verification of username/password combinations against an LDAP server, optionally
over SSL/TLS. Check out the API documentation for more information on how to do this.
- Home
- Requirements
- spray-server
- ... Getting Started
- ... Key Concepts
- ...... Request Lifecycle
- ...... Routes
- ...... Directives
- ...... Composing Directives
- ...... Rejections
- ...... Marshalling and Unmarshalling
- ... Predefined Directives
- ...... Method Filters
- ...... Path Filters
- ...... Parameter Filters
- ...... Form-Field Filters
- ...... Marshalling/Unmarshalling
- ...... Caching
- ...... Detach
- ...... Encoding/Decoding
- ...... Authentication/Authorization
- ...... File and Resource Directives
- ...... Misc Directives
- ... Advanced Topics
- ...... Case Class Extraction
- ...... Custom Directives
- ...... Custom Media Types
- ...... Custom Error Responses
- ... Configuration
- ... Testing
- ... Example Projects
- spray-client
- Patch Policy
- Credits
- Sponsors