Spray Authentication method with BasicAuth - scala

Spray is hard!! I now know that my knowledge on HTTP protocol is not nearly enough and API design isn't easy. However, I still very much want my practice app to work. I'm writing this authentication for POST/PUT/DELETE method. It appears that there are at least two ways to do this: BasicAuth or write a custom directive.
I found this article:
BasicAuth: https://github.com/jacobus/s4/blob/master/src/main/scala/s4/rest/S4Service.scala
I'm trying it out because it looks simple.
The compile and run stages are fine, and the server runs. However, when I'm trying to send a PUT request to test the implementation (using Python's Httpie: http PUT 127.0.0.1:8080/sec-company/casper username=username token=123), the feedback is:HTTP/1.1 404 Not Found
Here's my route:
pathPrefix("sec-company") {
path("casper") {
//first examine username and token
authenticate(BasicAuth(CustomUserPassAuthenticator, "company-security")) {userProfile =>
post { ctx =>
entity(as[Company.Company]) { company =>
complete {
company
}
}
}
}
Here is my implementation of UserPassAuthenticator:
object CustomUserPassAuthenticator extends UserPassAuthenticator[UserProfile] {
def apply(userPass: Option[UserPass]) = Promise.successful(
userPass match {
case Some(UserPass(user, token)) => getUserProfile(user, token)
case _ => None
}
).future
}
First of all, is this the right way to implement authentication? Second, where does UserPassAuthenticator find the username and password?? Can I send back a better HTTP header other than 404 to indicate failed authentication?
If this is far from correct, is there any tutorial on authentication that I can follow? TypeSafe's Spray templates are more about overall patterns and less about Spray's functionality!
Thank you!

I had the same problem, even after looking at https://github.com/spray/spray/wiki/Authentication-Authorization (which says it's for an older version of Akka but it still seems to apply) I came up with the following:
trait Authenticator {
def basicUserAuthenticator(implicit ec: ExecutionContext): AuthMagnet[AuthInfo] = {
def validateUser(userPass: Option[UserPass]): Option[AuthInfo] = {
for {
p <- userPass
user <- Repository.apiUsers(p.user)
if user.passwordMatches(p.pass)
} yield AuthInfo(user)
}
def authenticator(userPass: Option[UserPass]): Future[Option[AuthInfo]] = Future { validateUser(userPass) }
BasicAuth(authenticator _, realm = "Private API")
}
}
I mix in this trait into the Actor that runs the routes and then I call it like this:
runRoute(
pathPrefix("api") {
authenticate(basicUserAuthenticator) { authInfo =>
path("private") {
get {
authorize(authInfo.hasPermission("get") {
// ... and so on and so forth
}
}
}
}
}
}
The AuthInfo object returned by the validateUser method is passed as a parameter to the closure given to the authorize method. Here it is:
case class AuthInfo(user: ApiUser) {
def hasPermission(permission: String) = user.hasPermission(permission)
}
In Spray (and HTTP), authentication (determining whether you have a valid user) is separate from authorization (determining whether the user has access to a resource). In the ApiUser class I also store the set of permissions the user has. This is a simplified version, my hasPermission method is a bit more complex since I also parametrize permissions, so it's not just that a particular user has permission to do a get on a resource, he might have permission to read only some parts of that resource. You might make things very simple (any logged-in user can access any resource) or extremely complex, depending on your needs.
As to your question, when using HTTP BASIC authentication (the BasicAuth object), the credentials are passed in the request in an Authorization: header. Your HTTP library should take care of generating that for you. According to the HTTP standard, the server should return a 401 status code if the authentication was incorrect or not provided, or a 403 status code if the authentication was correct but the user doesn't have permissions to view the content.

Related

Akka Authentication with REST Http

I'm testing Json HTTP Routes with Akka Basic Authentication and scala. But the routes don't work as needed. Here is the code
First the secured route is not working with postman BasicAuth. I need to add an email"secured/test#test.com" to trigger the logged in action. Second the subroute basketactor is not working at all. Postman says unknown Route. So how can i make this work with Postman BasicAuth Http Header? What kind of request do i have to send just "secured or with email and password together.
Second how can i make the subroutes basketActor/add work.
~ Route.seal {
(pathPrefix("secured") & get) {
authenticateBasic(realm = "secure site", myUserPassAuthenticator) { email =>
path(Segment) { segment =>
var user = User.USER_LIST.find(_.id == segment)
if (user.isDefined)
complete(s"Hi '$email' you logged in")
else complete(s"login failed Please register at ${"users" / "register"}")
}
} ~ path("basketActor") {
(pathEnd & get) {
implicit val timeout = Timeout(3 seconds)
onSuccess((basketActor ? GetBasketProducts).mapTo[BasketList]) { res =>
complete(BasketContainer(products = res.products))
}
} ~ (path("add" / IntNumber) & post) { number =>
val product = Product.PRODUCT_LIST.find(_.id == number)
if (product.isDefined) {
product.foreach { product =>
basketActor ! AddProductToBasket(product)
// complete(Basket.BASKET_LIST ::= product)
}
complete(product)
} else complete("Not found")
}
}
}
}
def myUserPassAuthenticator(credentials: Credentials): Option[String] =
credentials match {
case p#Credentials.Provided(id) if p.verify("secret") => Some(id)
case _ => None
}
So my expected result should be the user calls the secured Route logs in and then he can access the basketActor route. But now the route is not accessible and the secured route is also not working
This is how Basic Authentication works in Akka HTTP:
The Authorization header in an HTTP Request is used for Basic Authentication, and it contains a username and password encoded using base64 encoding. The authenticateBasic route will extract this information from the header to create a Credentials object, and then pass this to your myUserPassAuthenticator method. This method should check that the username and password match and if it succeeds it should return an object representing that user in your system, or return None on failure.
The second (curried) argument to authenticateBasic is a function that takes the user data and returns a Route. So in your case the email argument will contain the id value returned by myUserPassAuthenticator. This function should return a route that contains all the paths that require authentication.
It is important to realise that there is no concept of being "logged in" with Basic Authentication. There are just authentication credentials that are either correct or incorrect. (If you want to have login/logout semantics you need to move to a token-based authentication mechanism).
So to make your routes work you need to move the logic that looks-up a user ID into myUserPassAuthenticator and return the user object as the result. As it happens you can just return the result of find because this already returns an Option[User].
You also need to make sure that all the authenticated routes are inside the authenticateBasic route. Currently the basketActor and add routes are at the same level and therefore are not authenticated.
A sample route would look like this. I have used concat rather than ~ as I think it is clearer.
pathPrefix("secured") {
authenticateBasic(realm = "secure site", myUserPassAuthenticator) { user =>
concat(
(pathEnd | pathSingleSlash) {
get {
complete(s"User $user Authenticated OK")
}
},
path("basketActor") {
(pathEnd & get) {
complete(s"basketActor for $user")
}
},
path("add" / IntNumber) { number =>
post {
complete(s"User $user posted $number")
}
}
)
}
}
Note that the user ID (email address) is not in the URL, it is in the request in the Basic Authentication header.
To test this with Postman you need to go create a GET with the URL .../secured or .../secured/basketActor, or a POST with the URL .../secured/add/1. Under the Auth tab you need to select Basic Authentication and add the credentials there.

How to do authentication using Akka HTTP

Looking for a good explanation on how to do authentication using akka HTTP. Given a route that looks like
val route =
path("account") {
authenticateBasic(realm = "some realm", myAuthenticator) { user =>
get {
encodeResponseWith(Deflate) {
complete {
//do something here
}
}
}
}
}
The documentation outlines a way, but then the pertinent part performing the actual authentication is omitted...
// backend entry points
def myAuthenticator: Authenticator[User] = ???
Where can I find an example implementation of such an authenticator? I have the logic already for authenticating a user given a user name and password, but what i can't figure out is how to get a username/password (or token containing both) from the HTTP request (or RequestContext).
Authenticator is just a function UserCredentials => Option[T], where UserCredentials in case of being (check with pattern matching) Provided have verifySecret(secret) method which you need to safely call and return Some (Some user for example) in case of success, like:
def myAuthenticator: Authenticator[User] = {
case p#Provided(username) =>
if(p.verifySecret(myGetSecret(username))) Some(username) else None
case Missing => None //you can throw an exeption here to get customized response otherwise it will be regular `CredentialsMissing` message
}
myGetSecret is your custom function which gets username and returns your secret (e.g. password), getting it possibly from database. verifySecret will securely compare (to avoid timing attack) provided password with your password from myGetSecret. Generally, "secret" is any hidden information (like hash of credentials or token) but in case of basic authentication it is just a plain password extracted from http headers.
If you need more customized approach - use authenticateOrRejectWithChallenge that gets HttpCredentials as an input, so you can extract provided password from there.
More info about authorization is in scaladocs.

Play Framework REST with basic authentication and SSL

I am new to this authentication area. I searched a lot but was not able to find a way to authenticate the REST calls made to the Play server. What are the various ways and best practice?
A very easy way is to use Action Composition. For a sample, take a look at this Gist provided by Guillaume Bort: https://gist.github.com/guillaumebort/2328236. If you want to use it in an async action, you can write something like:
def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request =>
request.headers.get("Authorization").flatMap { authorization =>
authorization.split(" ").drop(1).headOption.filter { encoded =>
new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
case u :: p :: Nil if u == username && password == p => true
case _ => false
}
}
}.map(_ => action(request)).getOrElse {
Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area""""))
}
}
SSL does not have anything to do with basic authentication. You can use HTTPS for API either directly or through a front-end HTTP server like ngnix. There are pretty good details in Play documentation on this subject.
basically, I have taken the answer from #centr and tried to make it a little more readable. See if you prefer this version of the same code. Tested thoroughly, works as expected.
def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request =>
val submittedCredentials: Option[List[String]] = for {
authHeader <- request.headers.get("Authorization")
parts <- authHeader.split(' ').drop(1).headOption
} yield new String(decodeBase64(parts.getBytes)).split(':').toList
submittedCredentials.collect {
case u :: p :: Nil if u == username && p == password => action(request)
}.getOrElse {
Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area""""))
}
}
If we are just talking about basic auth, you don't need any external module. Basically, you could implement it using action composition.
Here is a full example of it.
If you also need authorization, you could simply combine the previous example with Deadbolt. It will allow you to provide access to some group of clients and deny access to others.
SSL support does not have anything to do with the authentication. However, is explained in the Play Documentation
Read the following Readme/article: Securing Single Page Apps and REST Services and check out the corresponding sample application (same link).
It explains how to do what you're asking.
For Scala, Secure Social is probably the best estabilished solution. You will find plenty of documentation and examples at the given link.
You can also take a look at Play2-auth as another valid option.
You will find even more possibilities on Play 2 Modules list.
If you want/need to bake your own solution, it will probably still be useful to look into code of existing solutions for inspiration and ideas. Nevertheless, my general advice towards anything related with security is NOT to implement it yourself unless you really need it (and/or really know what you're doing).
BTW, there's absolutely nothing specific about REST here. You're essentially protecting your controllers methods, so it doesn't matter whether their invocation was triggered by a REST call or not.
A filter could be used as well. The following is based on Play 2.5.
import org.apache.commons.codec.binary.Base64
override def apply(nextFilter: RequestHeader => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {
val auth = requestHeader.headers.get("Authorization")
val invalidResult = Future.successful(
Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured"""")
)
if (auth.isEmpty) {
invalidResult
}
else {
val credentials = new String(Base64.decodeBase64(auth.get.split(" ").drop(1).head.getBytes)).split(":")
if (credentials.length < 2) {
invalidResult
}
else {
for {
authVerify <- verify(credentials(0), credentials(1))
r <- {
if (authVerify) {
nextFilter(requestHeader).map { result: Result => result }
}
else {
invalidResult
}
}
} yield {
r
}
}
}
}
def verify(username: String, password: String): Future[Boolean]

Token based authentication in Play filter & passing objects along

I've written an API based on Play with Scala and I'm quite happy with the results. I'm in a stage where I'm looking at optimising and refactoring the code for the next version of the API and I had a few questions, the most pressing of which is authentication and the way I manage authentication.
The product I've written deals with businesses, so exposing Username + Password with each request, or maintaining sessions on the server side weren't the best options. So here's how authentication works for my application:
User authenticates with username/password.
Server returns a token associated with the user (stored as a column in the user table)
Each request made to the server from that point must contain a token.
Token is changed when a user logs out, and also periodically.
Now, my implementation of this is quite straightforward – I have several forms for all the API endpoints, each one of which expects a token against which it looks up the user and then evaluates if the user is allowed to make the change in the request, or get the data. So each of the forms in the authenticated realm are forms that need a token, and then several other parameters depending on the API endpoint.
What this causes is repetition. Each one of the forms that I'm using has to have a verification part based on the token. And its obviously not the briefest way to go about it. I keep needing to replicate the same kind of code over and over again.
I've been reading up about Play filters and have a few questions:
Is token based authentication using Play filters a good idea?
Can a filter not be applied for a certain request path?
If I look up a user based on the supplied token in a filter, can the looked up user object be passed on to the action so that we don't end up repeating the lookup for that request? (see example of how I'm approaching this situation.)
case class ItemDelete(usrToken: String, id: Long) {
var usr: User = null
var item: Item = null
}
val itemDeleteForm = Form(
mapping(
"token" -> nonEmptyText,
"id" -> longNumber
) (ItemDelete.apply)(ItemDelete.unapply)
verifying("unauthorized",
del => {
del.usr = User.authenticateByToken(del.usrToken)
del.usr match {
case null => false
case _ => true
}
}
)
verifying("no such item",
del => {
if (del.usr == null) false
Item.find.where
.eq("id", del.id)
.eq("companyId", del.usr.companyId) // reusing the 'usr' object, avoiding multiple db lookups
.findList.toList match {
case Nil => false
case List(item, _*) => {
del.item = item
true
}
}
}
)
)
Take a look at Action Composition, it allows you to inspect and transform a request on an action. If you use a Play Filter then it will be run on EVERY request made.
For example you can make a TokenAction which inspects the request and if a token has been found then refine the request to include the information based on the token, for example the user. And if no token has been found then return another result, like Unauthorized, Redirect or Forbidden.
I made a SessionRequest which has a user property with the optionally logged in user, it first looks up an existing session from the database and then takes the attached user and passes it to the request
A filter (WithUser) will then intercept the SessionRequest, if no user is available then redirect the user to the login page
// Request which optionally has a user
class SessionRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest[A](request)
object SessionAction extends ActionBuilder[SessionRequest] with ActionTransformer[Request, SessionRequest] {
def transform[A](request: Request[A]): Future[SessionRequest[A]] = Future.successful {
val optionalJsonRequest: Option[Request[AnyContent]] = request match {
case r: Request[AnyContent] => Some(r)
case _ => None
}
val result = {
// Check if token is in JSON request
for {
jsonRequest <- optionalJsonRequest
json <- jsonRequest.body.asJson
sessionToken <- (json \ "auth" \ "session").asOpt[String]
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if the token is in a cookie
for {
cookie <- request.cookies.get("sessionid")
sessionToken = cookie.value
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if its added in the header
for {
header <- request.headers.get("sessionid")
session <- SessionRepository.findByToken(header)
} yield session
}
result.map(x => new SessionRequest(x.user, request)).getOrElse(new SessionRequest(None, request))
}
}
// Redirect the request if there is no user attached to the request
object WithUser extends ActionFilter[SessionRequest] {
def filter[A](request: SessionRequest[A]): Future[Option[Result]] = Future.successful {
request.user.map(x => None).getOrElse(Some(Redirect("http://website/loginpage")))
}
}
You can then use it on a action
def index = (SessionAction andThen WithUser) { request =>
val user = request.user
Ok("Hello " + user.name)
}
I hope this will give you an idea on how to use Action Composition
The people at Stormpath has a sample Play application providing authentication via their Backend Service. Some of its code could be useful to you.
It uses username/password rather than tokens, but it should not be complex to modify that.
They have followed this Play Document:
https://www.playframework.com/documentation/2.0.8/ScalaSecurity.
The specific implementation for this is here:
https://github.com/stormpath/stormpath-play-sample/blob/dev/app/controllers/MainController.scala.
This Controller handles authentication operations and provides the isAuthenticated action via the Secured Trait (relying on play.api.mvc.Security). This operation checks if the user is
authenticated and redirects him to the login screen if he is not:
/**
* Action for authenticated users.
*/
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) =
Security.Authenticated(email, onUnauthorized) { user =>
Action.async { request =>
email(request).map { login =>
f(login)(request)
}.getOrElse(Future.successful(onUnauthorized(request)))
}
}
Then, each controller that needs authenticated operations must use the
Secured Trait:
object MyController extends Controller with Secured
And those operations are "wrapped" with the IsAuthenticated action:
def deleteItem(key: String) = IsAuthenticated { username => implicit request =>
val future = Future {
MyModel.deleteItem(request.session.get("id").get, key)
Ok
}
future.map(
status => status
)
}
Note that the deleteItem operation does not need a username, only the key. However, the authentication information is automatically obtained from the session. So, the business' API does not get polluted with security-specific parameters.
BTW, that application seems to have never been officially released so consider this code a proof of concept.

Spray testing basicauth from js html

I have code like:
https://gist.github.com/daaatz/7665224
but dont know how to test request.
Trying mydomain/secured?user=John&password=p4ssw0rd etc but nothing works.
Can some one tell me or show example in js+html how to check is it working fine ?
Thanks
I've never used BasicAuth in Spray, so i'm not sure if this would be the complete answer, but i hope this will help you.
At first. in spray there is a great spray-testkit written on top of akka testkit. You should definitely check out SecurityDirectives test on github, this will show you how to test basic authentication. A little example, to make this simpler:
As for your route example, i would better edit to the following one:
val myRoute =
(path("secured") & get) {
authenticate(BasicAuth(myUserPassAuthenticator _, realm = "secure site")) {
userName => complete(s"The user is '$userName'")
}
}
}
Adding get directive will specify that this route expects a Get request and sealRoute is obsolete cause RejectionHandler and ExceptionHandler are provided implicitly with runRoute. It is used only in tests, if you want wo check exceptions/rejections.
Now in your tests you should construct auth entities, similar to the test one:
val challenge = `WWW-Authenticate`(HttpChallenge("Basic", "Realm"))
val doAuth = BasicAuth(UserPassAuthenticator[BasicUserContext] { userPassOption ⇒
Future.successful(Some(BasicUserContext(userPassOption.get.user)))
}, "Realm")
And you test case:
"simple auth test" in {
Get("security") ~> Authorization(BasicHttpCredentials("Alice", "")) ~> {
authenticate(doAuth) { echoComplete }
} ~> check { responseAs[String] === "BasicUserContext(Alice)" }
}
In Get("security") specifies that your test will send a Get request on "/security", then add Authorization to the test request, some action and the check part to test the request.
I've never tried to test BasicAuth, so there could be some mistakes.
I would look into CURL for testing routes in web applications, but I've also used the chrome extension Postman with great results as well.