nulab / scala-oauth2-provider

OAuth 2.0 server-side implementation written in Scala

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

client_credentials and password validation at the same DataHandler

dbalduini opened this issue · comments

I can't figure out how i should implement my DataHandler to validate both Client and User.

In the sample provided in the Play 2.2 sample, we have both User and Client entities, but in the findClientUser method, None is returned.

  def findClientUser(clientId: String, clientSecret: String, scope: Option[String]): Option[User] = {
    None // Not implemented yet
  }

The question is, how can i return a user, if i am trying to authenticate with a Client (client_credentials) ?

You need to create an entity that associates User and Client.
The entity will have primary key of User and client_id.
The client_id will be created as unique key each user.

By doing so, you can get associated User from the entity by clientId on findClientUser.

My scenario is a mobile app. Before the user has an account, the client needs to access one route, and to protect this specific route, i am using the client credentials grant. So i don't have an User to return.

In that case, should i make the user primary key as an option? because i am using the Client Credentials Grant when the client needs to access a route of the server, and Resource Owner Grant for all other routes that requires an User.

Here are my custom Actions.

  object UserAction extends ActionBuilder[ResourceOwnerRequest] {
    override def invokeBlock[A](request: Request[A], block: ResourceOwnerRequest[A] => Future[Result]): Future[Result] = {
      authorize(userHandler) { authInfo =>
        block(new ResourceOwnerRequest(authInfo, request))
      }(request)
    }
  }

  object ClientAction extends ActionBuilder[ClientCredentialsRequest] {
    override def invokeBlock[A](request: Request[A], block: ClientCredentialsRequest[A] => Future[Result]): Future[Result] = {
      authorize(clientHandler) { authInfo =>
        block(new ClientCredentialsRequest[A](authInfo, request))
      }(request)
    }
  }

I had to do that because i don't have an User to return. Here are my models

case class SessionUser(id: UserId, club: Club, device: Device, createdAt: DateTime, expires: Int = 1800) extends User with OAuthUser {
  def name = id.stringify
}

case class Client(_id: String, secret: Option[String], grantTypes: List[String], redirectUri: Option[String] = None, scope: List[String] = Nil) extends OAuthUser {
  def name = _id
  val expires = 1800
}

But i got stuck in the OAuth2AsyncProvider trait, where i should have only one DataHandler.

trait OAuth2Controller extends Controller with OAuth2AsyncProvider {
  this: DataHandlerComponent =>
  def accessToken = Action.async {
    implicit request =>
      issueAccessToken(userHandler)
  }
}

I can make a turn around, but i want to know if there is a better way that i am probably missing.

I don't know well how to let you authorize the user before has an account.
But I think the user model have primary key as an option or return a unique dummy user is a better way.
This library is assuming that we already have users in the system.

I did some work in a local branch and i come to this solution

https://github.com/dbalduini/scala-oauth2-provider/tree/break-client-auth

Can you take a look?

I think we can come to something way better than this, but for now this is working fine for me. Also, i think the better solution is to break:

  • Authorization phases
  • Access to Protected Resource phase

The main features i reached are:

def issueAccessToken[A, U](dataHandler: DataHandler[U])(implicit request: Request[A]): Future[Result] 

def authorize[A, U](dataHandler: ResourceHandler[U])(callback: AuthInfo[U] => Future[Result])(implicit request: Request[A]): Future[Result]
trait AuthServiceComponent {
  val passwordHandler: ResourceHandler[SessionUser]
  val clientHandler: ResourceHandler[Client]
  val dataHandler: DataHandler[OAuthUser]
}
  object UserAction extends ActionBuilder[PasswordRequest] {
    override def invokeBlock[A](request: Request[A], block: PasswordRequest[A] => Future[Result]): Future[Result] = {
      authorize(passwordHandler) { authInfo =>
        block(new PasswordRequest(authInfo, request))
      }(request)
    }
  }

  object ClientAction extends ActionBuilder[ClientCredentialsRequest] {
    override def invokeBlock[A](request: Request[A], block: ClientCredentialsRequest[A] => Future[Result]): Future[Result] = {
      authorize(clientHandler) { authInfo =>
        block(new ClientCredentialsRequest[A](authInfo, request))
      }(request)
    }
  }
trait OAuth2Controller extends Controller with OAuth2AsyncProvider {
  this: AuthServiceComponent =>
  def accessToken = Action.async {
    implicit request =>
      issueAccessToken(dataHandler)
  }
}

I can have both Client and User. And my Client does not have and User in its context. In this way i have resources that can be accessed by the client before the User creates his account.

In my case, i have an mobile game app. At the user first time entering the game, the client automatically creates an User Account. But for this, the client has to authenticate with Client Credentials at the "User creation route".

Thanks for explanation and try to change the code.
I understand your problem.

Can you take a look?

LGTM

Point of your change is divided DataHandler into ResourceHandler.
But that changing is DataHandler interface will be broken.

Would you try to rename DataHandler to TokenHandler (or other name)?
Then, you make trait DataHandler extends TokenHander with ResourceHandler.
By doing this, everyone would be able to use this interface without break.

Thanks for the tips.

After reworking with what you said, here is the final result. #38

trait DataHandler[U] extends AuthorizationHandler[U] with ProtectedResourceHandler[U]

Still works for what i needed and will not break compatibility.

Many thanks for the PR.
I merged and published it. AuthorizationHandler is good naming.