-
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 many times 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 => Either[Rejection, U]
The GeneralAuthenticator does the actual job of either establishing a user identity (represented by an instance of U)
or rejecting the request. 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 thistype UserPassAuthenticator[U] = Option[(String, String)] => 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.
- 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