├── HEAD ├── system.properties ├── conf ├── play.plugins ├── routes ├── silhouette.conf └── application.conf ├── config ├── description ├── app ├── modules │ ├── .DS_Store │ └── cake │ │ ├── UserServiceModule.scala │ │ ├── AvatarServiceModule.scala │ │ ├── MailServiceModule.scala │ │ ├── CredentialsProviderModule.scala │ │ ├── AuthInfoServiceModule.scala │ │ ├── AuthenticatorServiceModule.scala │ │ ├── EnvironmentModule.scala │ │ └── SocialProviderModule.scala ├── views │ └── authentication │ │ └── mails │ │ └── welcomeEmail.scala.html ├── models │ ├── users │ │ ├── BaseInfo.scala │ │ └── User.scala │ ├── authorizations │ │ └── Roles.scala │ └── daos │ │ ├── OAuth1InfoDAO.scala │ │ ├── OAuth2InfoDAO.scala │ │ └── PasswordInfoDAO.scala ├── security │ ├── models │ │ ├── SignUp.scala │ │ ├── Token.scala │ │ └── SocialAuth.scala │ └── formatters │ │ └── json │ │ ├── OAuth1InfoFormats.scala │ │ ├── PasswordInfoFormats.scala │ │ ├── CredentialFormats.scala │ │ └── OAuth2InfoFormats.scala ├── formatters │ └── json │ │ ├── CredentialFormat.scala │ │ ├── BaseInfoFormats.scala │ │ ├── SocialAuthFormat.scala │ │ └── UserFormats.scala ├── utils │ └── responses │ │ └── rest │ │ ├── Good.scala │ │ └── Bad.scala ├── controllers │ ├── RestApplicationController.scala │ └── security │ │ └── rest │ │ ├── RestCredentialsAuthController.scala │ │ ├── RestSignUpController.scala │ │ └── RestSocialAuthController.scala ├── services │ ├── MailService.scala │ ├── UserService.scala │ └── UserServicesImpl.scala └── Global.scala ├── public ├── images │ ├── favicon.png │ ├── silhouette.png │ └── providers │ │ ├── google.png │ │ ├── facebook.png │ │ └── twitter.png ├── javascripts │ └── hello.js └── stylesheets │ └── main.css ├── project ├── build.properties └── plugins.sbt ├── LICENSE ├── .gitignore └── README.md /HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.7 -------------------------------------------------------------------------------- /conf/play.plugins: -------------------------------------------------------------------------------- 1 | 1500:com.typesafe.plugin.CommonsMailerPlugin -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /app/modules/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/app/modules/.DS_Store -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/public/images/favicon.png -------------------------------------------------------------------------------- /public/images/silhouette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/public/images/silhouette.png -------------------------------------------------------------------------------- /public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /public/images/providers/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/public/images/providers/google.png -------------------------------------------------------------------------------- /public/images/providers/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/public/images/providers/facebook.png -------------------------------------------------------------------------------- /public/images/providers/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalek/silhouette-rest-seed/HEAD/public/images/providers/twitter.png -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Tue Jul 15 10:15:33 CEST 2014 3 | template.uuid=af582966-096a-43df-a504-5ddae8d4ebf1 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.1") 5 | -------------------------------------------------------------------------------- /app/modules/cake/UserServiceModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import services.UserServiceInMemory 4 | 5 | /** 6 | * Provides the user service 7 | */ 8 | trait UserServiceModule { 9 | 10 | lazy val userService = new UserServiceInMemory 11 | 12 | } -------------------------------------------------------------------------------- /app/views/authentication/mails/welcomeEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(user: models.users.User)(implicit request: RequestHeader, lang: Lang) 2 | 3 | 4 |

Welcome @user.info.fullName.getOrElse("???"),

5 |

6 | Your new account is ready!! 7 | 8 | -------------------------------------------------------------------------------- /app/models/users/BaseInfo.scala: -------------------------------------------------------------------------------- 1 | package models.users 2 | 3 | /** 4 | * Base info of an user 5 | */ 6 | case class BaseInfo( 7 | firstName: Option[String], 8 | lastName: Option[String], 9 | fullName: Option[String], 10 | gender: Option[String]) { 11 | 12 | } -------------------------------------------------------------------------------- /app/security/models/SignUp.scala: -------------------------------------------------------------------------------- 1 | package security.models 2 | 3 | /** 4 | * Case class for signUp element 5 | */ 6 | case class SignUp( 7 | password: String, 8 | identifier: String, 9 | firstName: Option[String], 10 | lastName: Option[String], 11 | fullName: Option[String]) -------------------------------------------------------------------------------- /app/security/formatters/json/OAuth1InfoFormats.scala: -------------------------------------------------------------------------------- 1 | package security.formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.impl.providers.OAuth1Info 6 | 7 | object OAuth1InfoFormats { 8 | 9 | implicit val restFormat = ( 10 | (__ \ "token").format[String] ~ 11 | (__ \ "secret").format[String])(OAuth1Info.apply, unlift(OAuth1Info.unapply)) 12 | } -------------------------------------------------------------------------------- /app/modules/cake/AvatarServiceModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import com.mohiva.play.silhouette.impl.services._ 4 | import com.mohiva.play.silhouette.api.util.HTTPLayer 5 | 6 | /** 7 | * Provides the avatar service. 8 | * 9 | * @param httpLayer The HTTP layer implementation. 10 | */ 11 | trait AvatarServiceModule { 12 | 13 | def httpLayer: HTTPLayer 14 | 15 | lazy val avatarService = new GravatarService(httpLayer) 16 | 17 | } -------------------------------------------------------------------------------- /app/formatters/json/CredentialFormat.scala: -------------------------------------------------------------------------------- 1 | package formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.util.Credentials 6 | 7 | /** 8 | * 9 | */ 10 | object CredentialFormat { 11 | 12 | implicit val restFormat = ( 13 | (__ \ "identifier").format[String] ~ 14 | (__ \ "password").format[String])(Credentials.apply, unlift(Credentials.unapply)) 15 | 16 | } -------------------------------------------------------------------------------- /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | h1 { 5 | text-align: center; 6 | font-size: 30px; 7 | } 8 | .starter-template { 9 | padding: 40px 15px; 10 | } 11 | 12 | fieldset { 13 | margin-top: 100px; 14 | text-align: center; 15 | } 16 | .social-providers, 17 | .sign-in-now { 18 | margin-top: 20px; 19 | } 20 | 21 | .user { 22 | margin-top: 50px; 23 | } 24 | .user .data { 25 | margin-top: 10px; 26 | } 27 | -------------------------------------------------------------------------------- /app/security/formatters/json/PasswordInfoFormats.scala: -------------------------------------------------------------------------------- 1 | package security.formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.util.PasswordInfo 6 | 7 | object PasswordInfoFormats { 8 | 9 | implicit val restFormat = ( 10 | (__ \ "hasher").format[String] ~ 11 | (__ \ "password").format[String] ~ 12 | (__ \ "salt").formatNullable[String])(PasswordInfo.apply, unlift(PasswordInfo.unapply)) 13 | } -------------------------------------------------------------------------------- /app/security/formatters/json/CredentialFormats.scala: -------------------------------------------------------------------------------- 1 | package security.formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.util.Credentials 6 | 7 | /** 8 | * Contain all format for com.mohiva.play.silhouette.api.providers.Credentials type 9 | */ 10 | object CredentialFormat { 11 | 12 | implicit val restFormat = ( 13 | (__ \ "identifier").format[String] ~ 14 | (__ \ "password").format[String])(Credentials.apply, unlift(Credentials.unapply)) 15 | 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /app/models/users/User.scala: -------------------------------------------------------------------------------- 1 | package models.users 2 | 3 | import com.mohiva.play.silhouette.api.Identity 4 | import com.mohiva.play.silhouette.api.LoginInfo 5 | import java.util.UUID 6 | 7 | import models.authorizations._ 8 | 9 | /** 10 | * A user of this platform 11 | */ 12 | case class User( 13 | id: String = UUID.randomUUID.toString, 14 | loginInfo: LoginInfo, 15 | socials: Option[Seq[LoginInfo]] = None, 16 | email: Option[String], 17 | username: Option[String], 18 | avatarUrl: Option[String], 19 | info: BaseInfo, 20 | roles: Set[Role] = Set(SimpleUser)) extends Identity { 21 | 22 | } 23 | 24 | object User { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/security/formatters/json/OAuth2InfoFormats.scala: -------------------------------------------------------------------------------- 1 | package security.formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import play.api.libs.json.Json.JsValueWrapper 6 | import com.mohiva.play.silhouette.impl.providers.OAuth2Info 7 | 8 | object OAuth2InfoFormats { 9 | 10 | implicit val restFormat = ( 11 | (__ \ "accessToken").format[String] ~ 12 | (__ \ "tokenType").formatNullable[String] ~ 13 | (__ \ "expiresIn").formatNullable[Int] ~ 14 | (__ \ "refreshToken").formatNullable[String] ~ 15 | (__ \ "params").formatNullable[Map[String, String]])(OAuth2Info.apply, unlift(OAuth2Info.unapply)) 16 | } -------------------------------------------------------------------------------- /app/formatters/json/BaseInfoFormats.scala: -------------------------------------------------------------------------------- 1 | package formatters 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.LoginInfo 6 | 7 | import models.users.BaseInfo 8 | 9 | /** 10 | * This object contains all format for User class 11 | */ 12 | object BaseInfoFormats { 13 | 14 | val storageFormat = ( 15 | (__ \ "firstName").formatNullable[String] ~ 16 | (__ \ "lastName").formatNullable[String] ~ 17 | (__ \ "fullName").formatNullable[String] ~ 18 | (__ \ "gender").formatNullable[String])(BaseInfo.apply _, unlift(BaseInfo.unapply _)) 19 | 20 | val restFormat = storageFormat 21 | 22 | } -------------------------------------------------------------------------------- /app/security/models/Token.scala: -------------------------------------------------------------------------------- 1 | package security.models 2 | 3 | import org.joda.time.DateTime 4 | import play.api.libs.json._ 5 | 6 | /** 7 | * This class represent token 8 | * 9 | * @param token Id of token 10 | * @param expiresOn The expiration time 11 | */ 12 | case class Token(token: String, expiresOn: DateTime) 13 | 14 | /** 15 | * Companion object, contain format for Json 16 | */ 17 | object Token { 18 | 19 | 20 | implicit val jodaDateWrites: Writes[org.joda.time.DateTime] = new Writes[org.joda.time.DateTime] { 21 | def writes(d: org.joda.time.DateTime): JsValue = JsString(d.toString) 22 | } 23 | 24 | implicit val restFormat = Json.format[Token] 25 | 26 | } -------------------------------------------------------------------------------- /app/formatters/json/SocialAuthFormat.scala: -------------------------------------------------------------------------------- 1 | package formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | 6 | /** 7 | * Generic class for Rest Social authentication 8 | */ 9 | case class SocialAuth( 10 | token: String, 11 | secret: Option[String]) 12 | 13 | /** 14 | * Companion object 15 | */ 16 | object SocialAuth { 17 | implicit val restFormat = SocialAuthFormat.restFormat 18 | } 19 | 20 | /** 21 | * Formatter for SocialAuth class 22 | */ 23 | object SocialAuthFormat { 24 | 25 | implicit val restFormat = ( 26 | (__ \ "token").format[String] ~ 27 | (__ \ "secret").formatNullable[String])(SocialAuth.apply, unlift(SocialAuth.unapply)) 28 | 29 | } -------------------------------------------------------------------------------- /app/modules/cake/MailServiceModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import play.api.mvc.RequestHeader 4 | import play.api.i18n.Lang 5 | import services._ 6 | import models.users._ 7 | 8 | /** 9 | * Provides the mail service. 10 | * 11 | */ 12 | trait MailServiceModule { 13 | 14 | class SimpleMailService extends MailService[User] { 15 | 16 | def sendWelcomeEmail(user: User)(implicit request: RequestHeader, lang: Lang) = { 17 | val html = views.html.authentication.mails.welcomeEmail(user)(request, lang) 18 | val txtAndHtml = (None, Some(html)) 19 | sendEmail("Welcome!!!!", user.email.get, txtAndHtml) 20 | } 21 | 22 | } 23 | 24 | lazy val mailService = new SimpleMailService 25 | 26 | } -------------------------------------------------------------------------------- /app/modules/cake/CredentialsProviderModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import com.mohiva.play.silhouette.impl.providers.CredentialsProvider 4 | import com.mohiva.play.silhouette.api.services.AuthInfoService 5 | import com.mohiva.play.silhouette.api.util.PasswordHasher 6 | 7 | /** 8 | * Provides the credentials provider. 9 | * 10 | * @param authInfoService The auth info service implemenetation. 11 | * @param passwordHasher The default password hasher implementation. 12 | */ 13 | trait CredentialsProviderModule { 14 | 15 | def authInfoService: AuthInfoService 16 | def passwordHasher: PasswordHasher 17 | 18 | lazy val credentialsProvider = new CredentialsProvider(authInfoService, passwordHasher, Seq(passwordHasher)) 19 | 20 | } -------------------------------------------------------------------------------- /app/security/models/SocialAuth.scala: -------------------------------------------------------------------------------- 1 | package security.models 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.util.Credentials 6 | 7 | /** 8 | * Generic class for Rest Social authentication 9 | */ 10 | case class SocialAuth( 11 | token: String, 12 | expiresIn: Option[Int], 13 | secret: Option[String]) 14 | 15 | /** 16 | * Companion object 17 | */ 18 | object SocialAuth { 19 | implicit val restFormat = SocialAuthFormat.restFormat 20 | } 21 | 22 | /** 23 | * Formatter for SocialAuth class 24 | */ 25 | object SocialAuthFormat { 26 | 27 | implicit val restFormat = ( 28 | (__ \ "accessToken").format[String] ~ 29 | (__ \ "expiresIn").formatNullable[Int] ~ 30 | (__ \ "secret").formatNullable[String])(SocialAuth.apply, unlift(SocialAuth.unapply)) 31 | 32 | } -------------------------------------------------------------------------------- /conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | GET / controllers.RestApplicationController.index 6 | GET /onlygodoruser controllers.RestApplicationController.onlyGodOrUser 7 | 8 | # Login/SignUp 9 | POST /auth/signin/credentials controllers.security.rest.RestCredentialsAuthController.authenticate 10 | POST /auth/signin/:provider controllers.security.rest.RestSocialAuthController.authenticate(provider) 11 | POST /auth/link/:provider controllers.security.rest.RestSocialAuthController.link(provider) 12 | POST /auth/signup controllers.security.rest.RestSignUpController.signUp 13 | GET /auth/signout controllers.security.rest.RestSignUpController.signOut 14 | 15 | # Map static resources from the /public folder to the /assets URL path 16 | GET /assets/*file controllers.Assets.at(path="/public", file) -------------------------------------------------------------------------------- /app/modules/cake/AuthInfoServiceModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO 4 | import com.mohiva.play.silhouette.impl.services.DelegableAuthInfoService 5 | import com.mohiva.play.silhouette.api.util.PasswordInfo 6 | import com.mohiva.play.silhouette.impl.providers.OAuth1Info 7 | import com.mohiva.play.silhouette.impl.providers.OAuth2Info 8 | 9 | /** 10 | * Provides the auth info service. 11 | * 12 | * @param passwordInfoDAO The implementation of the delegable password auth info DAO. 13 | * @param oauth1InfoDAO The implementation of the delegable OAuth1 auth info DAO. 14 | * @param oauth2InfoDAO The implementation of the delegable OAuth2 auth info DAO. 15 | */ 16 | trait AuthInfoServiceModule { 17 | 18 | def passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo] 19 | def oauth1InfoDAO: DelegableAuthInfoDAO[OAuth1Info] 20 | def oauth2InfoDAO: DelegableAuthInfoDAO[OAuth2Info] 21 | 22 | lazy val authInfoService = new DelegableAuthInfoService(passwordInfoDAO, oauth1InfoDAO, oauth2InfoDAO) 23 | 24 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | ### Scala ### 13 | *.class 14 | *.log 15 | 16 | # sbt specific 17 | .cache 18 | .history 19 | .lib/ 20 | dist/* 21 | target/ 22 | lib_managed/ 23 | src_managed/ 24 | project/boot/ 25 | project/plugins/project/ 26 | 27 | # Scala-IDE specific 28 | .scala_dependencies 29 | .worksheet 30 | 31 | 32 | ### SBT ### 33 | # Simple Build Tool 34 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 35 | 36 | target/ 37 | lib_managed/ 38 | src_managed/ 39 | project/boot/ 40 | .history 41 | .cache 42 | 43 | 44 | ### PlayFramework ### 45 | # Ignore Play! working directory # 46 | bin/ 47 | /db 48 | .eclipse 49 | /lib/ 50 | /logs/ 51 | /modules 52 | /project/project 53 | /project/target 54 | /target 55 | tmp/ 56 | test-result 57 | server.pid 58 | *.iml 59 | *.eml 60 | /dist/ 61 | .cache 62 | 63 | 64 | /.classpath 65 | /.project 66 | /.settings 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/utils/responses/rest/Good.scala: -------------------------------------------------------------------------------- 1 | package utils.responses.rest 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | 6 | /** 7 | * An util class, represent a good response, it's all right 8 | * 9 | * @param message 10 | */ 11 | class Good(val message: JsValue) { 12 | def status = "ok" 13 | } 14 | 15 | /** 16 | * Companion object for Good class 17 | */ 18 | object Good { 19 | 20 | def apply(message: String) = new Good(JsString(message)) 21 | def apply(message: JsValue) = new Good(message) 22 | def unapply(good: Good) = Some((good.status, good.message)) 23 | 24 | /** 25 | * Rest format 26 | */ 27 | implicit val restFormat: Format[Good] = { 28 | /** because of single value of read, i have to do map, it's a bug of play's json library, but don't worry ;)*/ 29 | val reads: Reads[Good] = ( 30 | (__ \ "message").read[JsValue]).map(m => Good.apply(m)) 31 | 32 | import play.api.libs.json.Writes._ 33 | val writes: Writes[Good] = ( 34 | (__ \ "status").write[String] ~ 35 | (__ \ "message").write[JsValue])(unlift(Good.unapply _)) 36 | 37 | Format(reads, writes) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/utils/responses/rest/Bad.scala: -------------------------------------------------------------------------------- 1 | package utils.responses.rest 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | 6 | /** 7 | * An util class, represent a bad response, not good 8 | * 9 | * @param code of error 10 | * @param error an object that expone the errors 11 | */ 12 | class Bad(val code: Option[Int], val error: JsValue) { 13 | def status = "ko" 14 | } 15 | 16 | /** 17 | * Companion object for Good class 18 | */ 19 | object Bad { 20 | 21 | def apply(code: Option[Int] = None, message: String) = new Bad(code, JsString(message)) 22 | def apply(code: Option[Int], message: JsValue) = new Bad(code, message) 23 | def apply(message: JsValue) = new Bad(None, message) 24 | def unapply(bad: Bad) = Some((bad.status, bad.code, bad.error)) 25 | 26 | /** 27 | * Rest format 28 | */ 29 | implicit val restFormat: Format[Bad] = { 30 | val reader: Reads[Bad] = ( 31 | (__ \ "code").readNullable[Int] ~ 32 | (__ \ "error").read[JsValue])(Bad.apply(_, _)) 33 | 34 | val writer: Writes[Bad] = ( 35 | (__ \ "status").write[String] ~ 36 | (__ \ "code").writeNullable[Int] ~ 37 | (__ \ "error").write[JsValue])(unlift(Bad.unapply _)) 38 | 39 | Format(reader, writer) 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /app/models/authorizations/Roles.scala: -------------------------------------------------------------------------------- 1 | package models.authorizations 2 | 3 | import com.mohiva.play.silhouette.api.Authorization 4 | import models.users._ 5 | import play.api.i18n._ 6 | import play.api.mvc.RequestHeader 7 | 8 | /** 9 | * Check for authorization 10 | */ 11 | case class WithRole(role: Role) extends Authorization[User] { 12 | def isAuthorized(user: User)(implicit request: RequestHeader, lang: Lang) = user.roles match { 13 | case list: Set[Role] => list.contains(role) 14 | case _ => false 15 | } 16 | 17 | } 18 | /** 19 | * Trait for all roles 20 | */ 21 | trait Role { 22 | def name: String 23 | } 24 | 25 | /** 26 | * Companion object 27 | */ 28 | object Role { 29 | 30 | def apply(role: String): Role = role match { 31 | case God.name => God 32 | case Admin.name => Admin 33 | case SimpleUser.name => SimpleUser 34 | case _ => Unknown 35 | } 36 | 37 | def unapply(role: Role): Option[String] = Some(role.name) 38 | 39 | } 40 | 41 | /** 42 | * Administration role 43 | */ 44 | object God extends Role { 45 | val name = "god" 46 | } 47 | 48 | /** 49 | * Administration role 50 | */ 51 | object Admin extends Role { 52 | val name = "admin" 53 | } 54 | 55 | /** 56 | * Normal user role 57 | */ 58 | object SimpleUser extends Role { 59 | val name = "user" 60 | } 61 | 62 | /** 63 | * The generic unknown role 64 | */ 65 | object Unknown extends Role { 66 | val name = "-" 67 | } -------------------------------------------------------------------------------- /conf/silhouette.conf: -------------------------------------------------------------------------------- 1 | silhouette { 2 | 3 | # Authenticator settings 4 | authenticator.cookieName="id" 5 | authenticator.cookiePath="/" 6 | authenticator.secureCookie=false 7 | authenticator.httpOnlyCookie=true 8 | authenticator.authenticatorIdleTimeout=1800 9 | authenticator.cookieAbsoluteTimeout=43200 10 | authenticator.authenticatorExpiry=43200 11 | #authenticator.headerName="Authenticate" 12 | 13 | # Facebook provider 14 | facebook.authorizationURL="https://graph.facebook.com/oauth/authorize" 15 | facebook.accessTokenURL="https://graph.facebook.com/oauth/access_token" 16 | facebook.redirectURL="http://localhost:9000/authenticate/facebook" 17 | facebook.clientID="" 18 | facebook.clientSecret="" 19 | facebook.scope="email" 20 | 21 | # Google provider 22 | google.authorizationURL="https://accounts.google.com/o/oauth2/auth" 23 | google.accessTokenURL="https://accounts.google.com/o/oauth2/token" 24 | google.redirectURL="http://localhost:9000/authenticate/google" 25 | google.clientID="" 26 | google.clientSecret="" 27 | google.scope="profile email" 28 | 29 | # Twitter provider 30 | twitter.requestTokenURL="https://twitter.com/oauth/request_token" 31 | twitter.accessTokenURL="https://twitter.com/oauth/access_token" 32 | twitter.authorizationURL="https://twitter.com/oauth/authenticate" 33 | twitter.callbackURL="http://localhost:9000/authenticate/twitter" 34 | twitter.consumerKey="" 35 | twitter.consumerSecret="" 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/formatters/json/UserFormats.scala: -------------------------------------------------------------------------------- 1 | package formatters.json 2 | 3 | import play.api.libs.json._ 4 | import play.api.libs.functional.syntax._ 5 | import com.mohiva.play.silhouette.api.LoginInfo 6 | import models.authorizations._ 7 | import models.users._ 8 | 9 | /** 10 | * This object contains all format for User class 11 | */ 12 | object UserFormats { 13 | 14 | val restFormat = { 15 | implicit val baseInfoFormat = formatters.BaseInfoFormats.restFormat 16 | 17 | val reader = ( 18 | (__ \ "id").read[String] ~ 19 | (__ \ "loginInfo").read[LoginInfo] ~ 20 | (__ \ "socials").readNullable(Reads.seq[LoginInfo]) ~ 21 | (__ \ "email").readNullable(Reads.email) ~ 22 | (__ \ "username").readNullable[String] ~ 23 | (__ \ "avatarUrl").readNullable[String] ~ 24 | (__ \ "info").read[BaseInfo] ~ 25 | (__ \ "roles").readNullable(Reads.set[String]).map { case Some(r) => r.map(Role.apply) case None => Set[Role](SimpleUser) })(User.apply _) 26 | 27 | val writer = ( 28 | (__ \ "id").write[String] ~ 29 | (__ \ "loginInfo").write[LoginInfo] ~ 30 | (__ \ "socials").writeNullable(Writes.seq[LoginInfo]) ~ 31 | (__ \ "email").writeNullable[String] ~ 32 | (__ \ "username").writeNullable[String] ~ 33 | (__ \ "avatarUrl").writeNullable[String] ~ 34 | (__ \ "info").write[BaseInfo] ~ 35 | (__ \ "roles").write(Writes.set[String]).contramap[Set[Role]](_.map(_.name)))(unlift(User.unapply _)) 36 | 37 | Format(reader, writer) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/controllers/RestApplicationController.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.libs.json._ 4 | import scala.concurrent.Future 5 | import play.api.libs.concurrent.Execution.Implicits._ 6 | import models.users.User 7 | import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator 8 | import com.mohiva.play.silhouette.api.Silhouette 9 | import modules.cake.HeaderEnvironmentModule 10 | import models.authorizations._ 11 | 12 | 13 | import utils.responses.rest._ 14 | 15 | /** 16 | * The basic application controller. 17 | * 18 | * @param env The Silhouette environment. 19 | */ 20 | class RestApplicationController extends Silhouette[User, JWTAuthenticator] with HeaderEnvironmentModule { 21 | 22 | implicit val userFormat = formatters.json.UserFormats.restFormat 23 | 24 | /** 25 | * Handles the index action. 26 | * 27 | * @return The result to display. 28 | */ 29 | def index = UserAwareAction.async { implicit request => 30 | request.identity match { 31 | case Some(user) => Future.successful(Ok(Json.toJson(user))) 32 | case None => Future.successful(Ok(Json.toJson(Good(message = "you are not logged! Login man!")))) 33 | } 34 | } 35 | 36 | /** 37 | * Handles the index action. 38 | * 39 | * @return The result to display. 40 | */ 41 | def onlyGodOrUser = SecuredAction(WithRole(God) || WithRole(SimpleUser)).async { implicit request => 42 | Future.successful(Ok(Json.obj("result" -> "Oh yess GOD"))) 43 | } 44 | 45 | } 46 | 47 | object RestApplicationController extends RestApplicationController -------------------------------------------------------------------------------- /app/models/daos/OAuth1InfoDAO.scala: -------------------------------------------------------------------------------- 1 | package models.daos 2 | 3 | import com.mohiva.play.silhouette.api.LoginInfo 4 | import com.mohiva.play.silhouette.impl.providers.OAuth1Info 5 | import com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO 6 | import scala.collection.mutable 7 | import scala.concurrent.Future 8 | import OAuth1InfoDAO._ 9 | 10 | /** 11 | * The DAO to store the OAuth1 information. 12 | */ 13 | class OAuth1InfoDAO extends DelegableAuthInfoDAO[OAuth1Info] { 14 | 15 | /** 16 | * Saves the OAuth1 info. 17 | * 18 | * @param loginInfo The login info for which the auth info should be saved. 19 | * @param authInfo The OAuth1 info to save. 20 | * @return The saved OAuth1 info or None if the OAuth1 info couldn't be saved. 21 | */ 22 | def save(loginInfo: LoginInfo, authInfo: OAuth1Info): Future[OAuth1Info] = { 23 | data += (loginInfo -> authInfo) 24 | Future.successful(authInfo) 25 | } 26 | 27 | /** 28 | * Finds the OAuth1 info which is linked with the specified login info. 29 | * 30 | * @param loginInfo The linked login info. 31 | * @return The retrieved OAuth1 info or None if no OAuth1 info could be retrieved for the given login info. 32 | */ 33 | def find(loginInfo: LoginInfo): Future[Option[OAuth1Info]] = { 34 | Future.successful(data.get(loginInfo)) 35 | } 36 | } 37 | 38 | /** 39 | * The companion object. 40 | */ 41 | object OAuth1InfoDAO { 42 | 43 | /** 44 | * The data store for the OAuth1 info. 45 | */ 46 | var data: mutable.HashMap[LoginInfo, OAuth1Info] = mutable.HashMap() 47 | } 48 | -------------------------------------------------------------------------------- /app/models/daos/OAuth2InfoDAO.scala: -------------------------------------------------------------------------------- 1 | package models.daos 2 | 3 | import com.mohiva.play.silhouette.api.LoginInfo 4 | import com.mohiva.play.silhouette.impl.providers.OAuth2Info 5 | import com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO 6 | import scala.collection.mutable 7 | import scala.concurrent.Future 8 | import OAuth2InfoDAO._ 9 | 10 | /** 11 | * The DAO to store the OAuth2 information. 12 | */ 13 | class OAuth2InfoDAO extends DelegableAuthInfoDAO[OAuth2Info] { 14 | 15 | /** 16 | * Saves the OAuth2 info. 17 | * 18 | * @param loginInfo The login info for which the auth info should be saved. 19 | * @param authInfo The OAuth2 info to save. 20 | * @return The saved OAuth2 info or None if the OAuth2 info couldn't be saved. 21 | */ 22 | def save(loginInfo: LoginInfo, authInfo: OAuth2Info): Future[OAuth2Info] = { 23 | data += (loginInfo -> authInfo) 24 | Future.successful(authInfo) 25 | } 26 | 27 | /** 28 | * Finds the OAuth2 info which is linked with the specified login info. 29 | * 30 | * @param loginInfo The linked login info. 31 | * @return The retrieved OAuth2 info or None if no OAuth2 info could be retrieved for the given login info. 32 | */ 33 | def find(loginInfo: LoginInfo): Future[Option[OAuth2Info]] = { 34 | Future.successful(data.get(loginInfo)) 35 | } 36 | } 37 | 38 | /** 39 | * The companion object. 40 | */ 41 | object OAuth2InfoDAO { 42 | 43 | /** 44 | * The data store for the OAuth2 info. 45 | */ 46 | var data: mutable.HashMap[LoginInfo, OAuth2Info] = mutable.HashMap() 47 | } 48 | -------------------------------------------------------------------------------- /app/models/daos/PasswordInfoDAO.scala: -------------------------------------------------------------------------------- 1 | package models.daos 2 | 3 | import com.mohiva.play.silhouette.api.LoginInfo 4 | import com.mohiva.play.silhouette.api.util.PasswordInfo 5 | import com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO 6 | import scala.collection.mutable 7 | import scala.concurrent.Future 8 | import PasswordInfoDAO._ 9 | 10 | /** 11 | * The DAO to store the password information. 12 | */ 13 | class PasswordInfoDAO extends DelegableAuthInfoDAO[PasswordInfo] { 14 | 15 | /** 16 | * Saves the password info. 17 | * 18 | * @param loginInfo The login info for which the auth info should be saved. 19 | * @param authInfo The password info to save. 20 | * @return The saved password info or None if the password info couldn't be saved. 21 | */ 22 | def save(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] = { 23 | data += (loginInfo -> authInfo) 24 | Future.successful(authInfo) 25 | } 26 | 27 | /** 28 | * Finds the password info which is linked with the specified login info. 29 | * 30 | * @param loginInfo The linked login info. 31 | * @return The retrieved password info or None if no password info could be retrieved for the given login info. 32 | */ 33 | def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = { 34 | play.Logger.debug(s"data: ${data}") 35 | Future.successful(data.get(loginInfo)) 36 | } 37 | } 38 | 39 | /** 40 | * The companion object. 41 | */ 42 | object PasswordInfoDAO { 43 | 44 | /** 45 | * The data store for the password info. 46 | */ 47 | var data: mutable.HashMap[LoginInfo, PasswordInfo] = mutable.HashMap() 48 | } -------------------------------------------------------------------------------- /app/services/MailService.scala: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import play.api.libs.concurrent.Akka 4 | import play.api.Play.current 5 | import play.api.mvc.RequestHeader 6 | import play.api.i18n.Lang 7 | import play.twirl.api.{ Txt, Html } 8 | import com.typesafe.plugin._ 9 | import scala.concurrent.duration._ 10 | import play.api.libs.concurrent.Execution.Implicits._ 11 | import akka.actor._ 12 | import com.mohiva.play.silhouette.api._ 13 | 14 | /** 15 | * 16 | */ 17 | trait MailService[I <: Identity] { 18 | 19 | /** 20 | * 21 | */ 22 | val fromAddress = current.configuration.getString("smtp.from").get 23 | 24 | def sendWelcomeEmail(user: I)(implicit request: RequestHeader, lang: Lang) 25 | 26 | // def sendPasswordResetEmail(user: I, token: String)(implicit request: RequestHeader, lang: Lang) 27 | // 28 | // def sendPasswordChangedNotice(user: I)(implicit request: RequestHeader, lang: Lang) 29 | 30 | /** 31 | * @param subject of the email 32 | * @param recipient of the email 33 | * @param body pair with Text and Html email 34 | */ 35 | def sendEmail(subject: String, recipient: String, body: (Option[Txt], Option[Html])) = { 36 | 37 | play.Logger.debug(s"[securesocial] sending email to $recipient") 38 | play.Logger.debug(s"[securesocial] mail = [$body]") 39 | 40 | Akka.system.scheduler.scheduleOnce(1 seconds) { 41 | val mail = use[MailerPlugin].email 42 | mail.setSubject(subject) 43 | mail.setRecipient(recipient) 44 | mail.setFrom(fromAddress) 45 | // the mailer plugin handles null / empty string gracefully 46 | mail.send(body._1.map(_.body).getOrElse(""), body._2.map(_.body).getOrElse("")) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/services/UserService.scala: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import scala.concurrent.Future 4 | import play.api.libs.json.{JsValue, JsNull} 5 | import com.mohiva.play.silhouette.api.LoginInfo 6 | import com.mohiva.play.silhouette.api.services.{ AuthInfo, IdentityService } 7 | import com.mohiva.play.silhouette.impl.providers.CommonSocialProfile 8 | 9 | import security.models.SignUp 10 | import models.users.User 11 | 12 | /** 13 | * Handles actions to users. 14 | */ 15 | trait UserService extends IdentityService[User] { 16 | 17 | /** 18 | * Create a user from login information and signup information 19 | * 20 | * @param loginInfo The information about login 21 | * @param signUp The information about User 22 | * @param avatarUrl string with url to avatar image 23 | * @param json all json with signup information 24 | */ 25 | def create(loginInfo: LoginInfo, signUp: SignUp, avatarUrl: Option[String] = None, json: JsValue = JsNull): Future[User] 26 | 27 | /** 28 | * Saves a user. 29 | * 30 | * @param user The user to save. 31 | * @return The saved user. 32 | */ 33 | def save(user: User): Future[User] 34 | 35 | /** 36 | * Saves the social profile for a user. 37 | * 38 | * If a user exists for this profile then update the user, otherwise create a new user with the given profile. 39 | * 40 | * @param profile The social profile to save. 41 | * @return The user for whom the profile was saved. 42 | */ 43 | def save[A <: AuthInfo](profile: CommonSocialProfile): Future[User] 44 | 45 | /** 46 | * Link a social social profile on a user. 47 | * 48 | * 49 | * @param profile The social profile to save. 50 | * @return The user for whom the profile was saved. 51 | */ 52 | def link[A <: AuthInfo](user: User, profile: CommonSocialProfile): Future[User] 53 | } -------------------------------------------------------------------------------- /app/modules/cake/AuthenticatorServiceModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import play.api.Play 4 | import play.api.Play.current 5 | /** mohiva module import */ 6 | import com.mohiva.play.silhouette.impl.authenticators._ 7 | import com.mohiva.play.silhouette.api.services.AuthenticatorService 8 | import com.mohiva.play.silhouette.api.util.CacheLayer 9 | import com.mohiva.play.silhouette.api.util.Clock 10 | import com.mohiva.play.silhouette.api.util.IDGenerator 11 | 12 | /** 13 | * Provides the Header authenticator service. 14 | * 15 | * @param cacheLayer The cache layer implementation. 16 | * @param idGenerator The ID generator used to create the authenticator ID. 17 | */ 18 | trait HeaderAuthenticatorServiceModule { 19 | 20 | def idGenerator: IDGenerator 21 | 22 | lazy val authenticatorService: AuthenticatorService[JWTAuthenticator] = { 23 | val settings = JWTAuthenticatorSettings( 24 | headerName = Play.configuration.getString("silhouette.authenticator.headerName").getOrElse { "X-Auth-Token" }, 25 | issuerClaim = Play.configuration.getString("silhouette.authenticator.issueClaim").getOrElse { "play-silhouette" }, 26 | encryptSubject = Play.configuration.getBoolean("silhouette.authenticator.encryptSubject").getOrElse { true }, 27 | authenticatorIdleTimeout = Play.configuration.getInt("silhouette.authenticator.authenticatorIdleTimeout"), // This feature is disabled by default to prevent the generation of a new JWT on every request 28 | authenticatorExpiry = Play.configuration.getInt("silhouette.authenticator.authenticatorExpiry").getOrElse { 12 * 60 * 60 }, 29 | sharedSecret = Play.configuration.getString("application.secret").get) 30 | new JWTAuthenticatorService( 31 | settings = settings, 32 | dao = None, 33 | idGenerator = idGenerator, 34 | clock = Clock()) 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/modules/cake/EnvironmentModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import com.mohiva.play.silhouette.impl.authenticators.{ CookieAuthenticator, JWTAuthenticator } 4 | import com.mohiva.play.silhouette.impl.util.BCryptPasswordHasher 5 | import com.mohiva.play.silhouette.impl.util.PlayCacheLayer 6 | import com.mohiva.play.silhouette.impl.util.SecureRandomIDGenerator 7 | import com.mohiva.play.silhouette.api.Environment 8 | import com.mohiva.play.silhouette.api.EventBus 9 | import com.mohiva.play.silhouette.api.util.PlayHTTPLayer 10 | import models.users.User 11 | import models.daos._ 12 | 13 | 14 | /** 15 | * Provides the Silhouette environment. 16 | * 17 | * @param userService The user service implementation. 18 | * @param authenticatorService The authentication service implementation. 19 | * @param eventBus The event bus instance. 20 | */ 21 | trait HeaderEnvironmentModule 22 | extends HeaderAuthenticatorServiceModule 23 | with UserServiceModule 24 | with AuthInfoServiceModule 25 | with CredentialsProviderModule 26 | with SocialProviderModule 27 | with MailServiceModule { 28 | 29 | /** 30 | * Configures the module. 31 | */ 32 | lazy val cacheLayer = new PlayCacheLayer 33 | lazy val httpLayer = new PlayHTTPLayer 34 | lazy val eventBus = EventBus() 35 | lazy val idGenerator = new SecureRandomIDGenerator 36 | lazy val passwordInfoDAO = new PasswordInfoDAO 37 | lazy val oauth1InfoDAO = new OAuth1InfoDAO 38 | lazy val oauth2InfoDAO = new OAuth2InfoDAO 39 | lazy val passwordHasher = new BCryptPasswordHasher 40 | 41 | implicit lazy val env: Environment[User, JWTAuthenticator] = { 42 | Environment[User, JWTAuthenticator]( 43 | userService, 44 | authenticatorService, 45 | Map( 46 | credentialsProvider.id -> credentialsProvider, 47 | facebookProvider.id -> facebookProvider), 48 | eventBus) 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="52KF8WYkAOM^]4>S" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Logger 35 | # ~~~~~ 36 | # You can also configure logback (http://logback.qos.ch/), 37 | # by providing an application-logger.xml file in the conf directory. 38 | 39 | # Root logger: 40 | logger.root=ERROR 41 | 42 | # Logger used by the framework: 43 | logger.play=INFO 44 | 45 | # Logger provided to your application: 46 | logger.application=DEBUG 47 | 48 | # ****************************************** # 49 | # *** Email configuration *** # 50 | # ****************************************** # 51 | # Email 52 | # ~~~~~ 53 | smtp.mock=true 54 | smtp { 55 | host="" #example: smtp.gmail.com 56 | port="" #example: 465 57 | ssl=true 58 | user="thisisatest@gmail.com" 59 | password="thisismypassword" 60 | from="thisisatest@gmail.com" 61 | } 62 | 63 | include "silhouette.conf" 64 | 65 | -------------------------------------------------------------------------------- /app/Global.scala: -------------------------------------------------------------------------------- 1 | import play.api._ 2 | import play.api.i18n.{ Messages, Lang } 3 | import play.api.mvc._ 4 | import play.api.mvc.{ Result, RequestHeader } 5 | import play.api.mvc.Rendering 6 | import play.api.mvc.RequestHeader 7 | import play.api.mvc.Results._ 8 | import play.api.GlobalSettings 9 | import play.api.libs.json._ 10 | import play.api.libs.concurrent.Execution.Implicits.defaultContext 11 | import com.mohiva.play.silhouette.api.{ Logger, SecuredSettings } 12 | import scala.concurrent.Future 13 | 14 | import utils.responses.rest._ 15 | 16 | /** 17 | * The global configuration. 18 | */ 19 | object Global extends GlobalSettings with SecuredSettings with Logger { 20 | 21 | /** 22 | * Called when a user is not authenticated. 23 | * 24 | * As defined by RFC 2616, the status code of the response should be 401 Unauthorized. 25 | * 26 | * @param request The request header. 27 | * @param lang The currently selected language. 28 | * @return The result to send to the client. 29 | */ 30 | override def onNotAuthenticated(request: RequestHeader, lang: Lang): Option[Future[Result]] = { 31 | //controllers.StaticResponse.onNotAuthenticated(request, lang) 32 | Some(Future { Unauthorized(Json.toJson(Bad(message = "credentials not correct"))) }) 33 | } 34 | 35 | /** 36 | * Called when a user is authenticated but not authorized. 37 | * 38 | * As defined by RFC 2616, the status code of the response should be 403 Forbidden. 39 | * 40 | * @param request The request header. 41 | * @param lang The currently selected language. 42 | * @return The result to send to the client. 43 | */ 44 | override def onNotAuthorized(request: RequestHeader, lang: Lang): Option[Future[Result]] = { 45 | //controllers.StaticResponse.onNotAuthorized(request, lang) 46 | Some(Future { Unauthorized(Json.toJson(Bad(message = "credentials not correct"))) }) 47 | } 48 | 49 | /** 50 | * When an exception accurs in yout application, the onError operation 51 | * will be called. The default is to use the internal framework error page: 52 | */ 53 | override def onError(request: RequestHeader, ex: Throwable) = { 54 | Future.successful { 55 | if (play.api.Play.current.mode == Mode.Dev) 56 | InternalServerError(Json.toJson(Bad(message = "Internal server error " + ex.getMessage))) 57 | else 58 | InternalServerError(Json.toJson(Bad(message = "Oh oh o.O"))) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/controllers/security/rest/RestCredentialsAuthController.scala: -------------------------------------------------------------------------------- 1 | package controllers.security.rest 2 | 3 | import scala.concurrent.Future 4 | import play.api.mvc._ 5 | import play.api.libs.json._ 6 | import play.api.libs.concurrent.Execution.Implicits._ 7 | import com.mohiva.play.silhouette.api._ 8 | import com.mohiva.play.silhouette.api.exceptions.ConfigurationException 9 | import com.mohiva.play.silhouette.impl.providers.CredentialsProvider 10 | import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator 11 | import com.mohiva.play.silhouette.impl.exceptions.IdentityNotFoundException 12 | import com.mohiva.play.silhouette.api.util.Credentials 13 | import modules.cake.HeaderEnvironmentModule 14 | import security.models._ 15 | import models.users.User 16 | 17 | /** 18 | * This controller manage authentication of an user by identifier and password 19 | */ 20 | class RestCredentialsAuthController extends Silhouette[User, JWTAuthenticator] 21 | with HeaderEnvironmentModule { 22 | 23 | // implicit format for transform json <==> Credential 24 | implicit val restCredentialFormat = security.formatters.json.CredentialFormat.restFormat 25 | 26 | /** 27 | * Authenticates a user against the credentials provider. 28 | * 29 | * receive json like this: 30 | * { 31 | * "identifier": "...", 32 | * "password": "..." 33 | * } 34 | * 35 | * @return The result to display. 36 | */ 37 | def authenticate = Action.async(parse.json[Credentials]) { implicit request => 38 | (env.providers.get(CredentialsProvider.ID) match { 39 | case Some(p: CredentialsProvider) => p.authenticate(request.body) 40 | case _ => Future.failed(new ConfigurationException(s"Cannot find credentials provider")) 41 | }).flatMap { loginInfo => 42 | userService.retrieve(loginInfo).flatMap { 43 | case Some(user) => env.authenticatorService.create(user.loginInfo).flatMap { authenticator => 44 | env.eventBus.publish(LoginEvent(user, request, request2lang)) 45 | env.authenticatorService.init(authenticator).flatMap { token => 46 | env.authenticatorService.embed(token, Future.successful { 47 | Ok(Json.toJson(Token(token = token, expiresOn = authenticator.expirationDate))) 48 | }) 49 | } 50 | } 51 | case None => 52 | Future.failed(new IdentityNotFoundException("Couldn't find user")) 53 | } 54 | }.recoverWith(exceptionHandler) 55 | } 56 | 57 | } 58 | 59 | object RestCredentialsAuthController extends RestCredentialsAuthController -------------------------------------------------------------------------------- /app/controllers/security/rest/RestSignUpController.scala: -------------------------------------------------------------------------------- 1 | package controllers.security.rest 2 | 3 | import play.api.mvc._ 4 | import play.api.libs.json._ 5 | import play.api.libs.concurrent.Execution.Implicits._ 6 | import com.mohiva.play.silhouette.api._ 7 | import com.mohiva.play.silhouette.impl.providers.CredentialsProvider 8 | import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator 9 | import modules.cake.{ HeaderEnvironmentModule, AvatarServiceModule } 10 | import scala.concurrent.Future 11 | import utils.responses.rest._ 12 | import security.models._ 13 | import models.users._ 14 | 15 | /** 16 | * This controller manage registration of an user 17 | */ 18 | class RestSignUpController extends Silhouette[User, JWTAuthenticator] 19 | with HeaderEnvironmentModule 20 | with AvatarServiceModule { 21 | 22 | /** 23 | * The formats for read json represent user 24 | */ 25 | implicit val restFormat = formatters.json.UserFormats.restFormat 26 | implicit val signUpFormat = Json.format[SignUp] 27 | 28 | /** 29 | * Registers a new user. 30 | * 31 | * receive call with json like this: 32 | * { 33 | * "password": "", 34 | * "identifier": "", 35 | * "firstName": "", 36 | * "lastName": "", 37 | * "fullName": "" 38 | * } 39 | * 40 | * @return The result to display. 41 | */ 42 | def signUp = Action.async(parse.json) { implicit request => 43 | request.body.validate[SignUp].map { signUp => 44 | val loginInfo = LoginInfo(CredentialsProvider.ID, signUp.identifier) 45 | (userService.retrieve(loginInfo).flatMap { 46 | case None => /* user not already exists */ 47 | val authInfo = passwordHasher.hash(signUp.password) 48 | for { 49 | avatar <- avatarService.retrieveURL(signUp.identifier) 50 | userToSave <- userService.create(loginInfo, signUp, avatar) 51 | user <- userService.save(userToSave) 52 | authInfo <- authInfoService.save(loginInfo, authInfo) 53 | authenticator <- env.authenticatorService.create(loginInfo) 54 | token <- env.authenticatorService.init(authenticator) 55 | result <- env.authenticatorService.embed(token, Future.successful { 56 | Ok(Json.toJson(Token(token = token, expiresOn = authenticator.expirationDate))) 57 | }) 58 | } yield { 59 | env.eventBus.publish(SignUpEvent(user, request, request2lang)) 60 | env.eventBus.publish(LoginEvent(user, request, request2lang)) 61 | mailService.sendWelcomeEmail(user) 62 | result 63 | } 64 | case Some(u) => /* user already exists! */ 65 | Future.successful(Conflict(Json.toJson(Bad(message = "user already exists")))) 66 | }) 67 | }.recoverTotal { 68 | case error => 69 | Future.successful(BadRequest(Json.toJson(Bad(message = JsError.toFlatJson(error))))) 70 | } 71 | } 72 | 73 | /** 74 | * Handles the Sign Out action. 75 | * 76 | * @return The result to display. 77 | */ 78 | def signOut = SecuredAction.async { implicit request => 79 | env.eventBus.publish(LogoutEvent(request.identity, request, request2lang)) 80 | request.authenticator.discard(Future.successful(Ok)) 81 | } 82 | } 83 | 84 | object RestSignUpController extends RestSignUpController -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Silhouette REST Seed 2 | ================================= 3 | 4 | Example project for Play Framework that use [Silhouette](https://github.com/mohiva/play-silhouette) for authentication and authorization, expose rest api for signup, signin and social authentication. 5 | 6 | ## Basic usage 7 | 8 | ### Sign-up 9 | 10 | ```bash 11 | curl -X POST http://localhost:9000/auth/signup -H 'Content-Type: application/json' -d '{"firstName": "Alessandro", "lastName": "Random", "identifier": "merle@test.it", "password": "ohmygodthispasswordisverystrong!"}' -v 12 | ``` 13 | 14 | ``` 15 | < HTTP/1.1 200 OK 16 | < Content-Type: application/json; charset=utf-8 17 | < X-Auth-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC... 18 | 19 | { 20 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...", 21 | "expiresOn": "2015-02-20T10:35:42.813+01:00" 22 | } 23 | ``` 24 | 25 | ### Sign-in 26 | 27 | _Not necessary just after the sign-up because you already have a valid token._ 28 | 29 | ```bash 30 | curl -X POST http://localhost:9000/auth/signin/credentials -H 'Content-Type: application/json' -d '{"identifier": "merle@test.it", "password": "ohmygodthispasswordisverystrong!"}' -v 31 | ``` 32 | 33 | ``` 34 | < HTTP/1.1 200 OK 35 | < Content-Type: application/json; charset=utf-8 36 | < X-Auth-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC... 37 | 38 | { 39 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...", 40 | "expiresOn": "2015-02-20T10:35:42.813+01:00" 41 | } 42 | ``` 43 | 44 | ### Check if a request is authenticated 45 | 46 | ```bash 47 | curl http://localhost:9000 -H 'X-Auth-Token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...' -v 48 | ``` 49 | 50 | ``` 51 | < HTTP/1.1 200 OK 52 | < Content-Type: application/json; charset=utf-8 53 | 54 | { 55 | "id":"0711b0ea-0935-4697-9b0d-6a1fa7233166", 56 | "loginInfo":{ 57 | "providerID":"credentials", 58 | "providerKey":"merle@test.it" 59 | }, 60 | "email":"merle@test.it", 61 | "info":{ 62 | "firstName":"Alessandro", 63 | "lastName":"Random", 64 | "fullName":"Alessandro Random" 65 | }, 66 | "roles":["user"] 67 | } 68 | ``` 69 | 70 | ### Secured Action with autorization 71 | 72 | _The token must belong to a user with Admin role_ 73 | 74 | ```bash 75 | curl http://localhost:9000/onlygodoruser -H 'X-Auth-Token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...' -v 76 | ``` 77 | 78 | ``` 79 | < HTTP/1.1 200 OK 80 | < Content-Type: application/json; charset=utf-8 81 | 82 | {"result":"Oh yess GOD"} 83 | ``` 84 | 85 | ## Features 86 | 87 | * Sign Up 88 | * Sign In (Credentials) 89 | * Authorization 90 | * Dependency Injection with Cake Pattern 91 | * Publishing Events 92 | * Avatar service 93 | * Mail service 94 | 95 | ## Documentation 96 | 97 | Consultate the [Silhouette documentation](http://docs.silhouette.mohiva.com/) for more information. If you need help with the integration of Silhouette into your project, don't hesitate and ask questions in our [mailing list](https://groups.google.com/forum/#!forum/play-silhouette) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/playframework). 98 | 99 | ## Next Features 100 | 101 | * Link logged user with one or more social profile (already done, but not tested yet!) 102 | * Custom avatar service 103 | 104 | # License 105 | 106 | The code is licensed under [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0). 107 | -------------------------------------------------------------------------------- /app/controllers/security/rest/RestSocialAuthController.scala: -------------------------------------------------------------------------------- 1 | package controllers.security.rest 2 | 3 | import utils.responses.rest._ 4 | import services.UserService 5 | import play.api.mvc._ 6 | import play.api.libs.json._ 7 | import play.api.libs.concurrent.Execution.Implicits._ 8 | import com.mohiva.play.silhouette.api._ 9 | import com.mohiva.play.silhouette.api.services._ 10 | import com.mohiva.play.silhouette.api.exceptions.ConfigurationException 11 | import com.mohiva.play.silhouette.api.services.AuthInfoService 12 | import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator 13 | import com.mohiva.play.silhouette.impl.providers._ 14 | import modules.cake.{ HeaderEnvironmentModule, AvatarServiceModule } 15 | 16 | import scala.concurrent.{ Future, Promise } 17 | 18 | import security.models._ 19 | import models.users.User 20 | 21 | /** 22 | * This controller manage authentication of an user by social service (like facebook and other) 23 | */ 24 | class RestSocialAuthController extends Silhouette[User, JWTAuthenticator] with HeaderEnvironmentModule { 25 | 26 | /** 27 | * Authenticates a user against a social provider. 28 | * 29 | * @param provider The ID of the provider to authenticate against. 30 | * @return The result to display. 31 | */ 32 | def authenticate(provider: String) = Action.async(parse.json) { implicit request => 33 | (env.providers.get(provider) match { 34 | case Some(p: SocialProvider with CommonSocialProfileBuilder) => 35 | p.authenticate().flatMap { 36 | case Left(result) => Future.successful(result) 37 | case Right(authInfo) => for { 38 | profile <- p.retrieveProfile(authInfo) 39 | user <- userService.save(profile) 40 | authInfo <- authInfoService.save(profile.loginInfo, authInfo) 41 | authenticator <- env.authenticatorService.create(user.loginInfo) 42 | token <- env.authenticatorService.init(authenticator) 43 | result <- env.authenticatorService.embed(token, Future.successful { 44 | Ok(Json.toJson(Token(token = token, expiresOn = authenticator.expirationDate))) 45 | }) 46 | } yield { 47 | env.eventBus.publish(LoginEvent(user, request, request2lang)) 48 | result 49 | } 50 | } 51 | case _ => Future.failed(new ConfigurationException(s"Cannot authenticate with unexpected social provider $provider")) 52 | }).recoverWith(exceptionHandler) 53 | } 54 | 55 | /** 56 | * Link social with a existing user. 57 | * 58 | * receive json like this: 59 | * { 60 | * "accessToken": "...", 61 | * "expiresIn": 0000, //optional 62 | * "secret": "..." //this is for OAuth1, for OAuth2 isn't request 63 | * } 64 | * 65 | * @param provider The ID of the provider to authenticate against. 66 | * @return The result to display. 67 | */ 68 | def link(provider: String) = SecuredAction.async(parse.json) { implicit request => 69 | request.body.validate[SocialAuth].map { socialAuth => 70 | (profileAndAuthInfo(provider, socialAuth).flatMap { 71 | case (profile: CommonSocialProfile, authInfo: AuthInfo) => 72 | for { 73 | user <- userService.link(request.identity, profile) 74 | authInfo <- authInfoService.save(profile.loginInfo, authInfo) 75 | } yield { 76 | Ok(Json.toJson(Good(message = "link with social completed!"))) 77 | } 78 | }).recoverWith(exceptionHandler) 79 | }.recoverTotal { 80 | case error => Future.successful(BadRequest(Json.obj("message" -> JsError.toFlatJson(error)))) 81 | } 82 | } 83 | 84 | /** 85 | * Util method to use for retrieve information from authInfo 86 | * 87 | * @param provider where retrieve information 88 | * @param socialAuth object where get auth information 89 | * @return a pair with CommonSocialProfile and AuthInfo 90 | */ 91 | protected def profileAndAuthInfo(provider: String, socialAuth: SocialAuth) = { 92 | env.providers.get(provider) match { 93 | case Some(p: OAuth1Provider with CommonSocialProfileBuilder) => //for OAuth1 provider type 94 | val authInfo = OAuth1Info(token = socialAuth.token, socialAuth.secret.get) 95 | p.retrieveProfile(authInfo).map(profile => (profile, authInfo)) 96 | case Some(p: OAuth2Provider with CommonSocialProfileBuilder) => //for OAuth2 provider type 97 | val authInfo = OAuth2Info(accessToken = socialAuth.token, expiresIn = socialAuth.expiresIn) 98 | p.retrieveProfile(authInfo).map(profile => (profile, authInfo)) 99 | case _ => Future.successful(new ConfigurationException(s"Cannot retrive information with unexpected social provider $provider")) 100 | } 101 | } 102 | 103 | } 104 | 105 | object RestSocialAuthController extends RestSocialAuthController -------------------------------------------------------------------------------- /app/services/UserServicesImpl.scala: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import java.util.UUID 4 | import play.api.libs.json._ 5 | import play.api.libs.concurrent.Execution.Implicits._ 6 | import com.mohiva.play.silhouette.api.LoginInfo 7 | import com.mohiva.play.silhouette.api.services.AuthInfo 8 | import com.mohiva.play.silhouette.impl.providers.CommonSocialProfile 9 | import scala.concurrent.Future 10 | import scala.collection.mutable 11 | 12 | import security.models.SignUp 13 | import models.users._ 14 | 15 | /** 16 | * BASIC IMPLEMENTATION 17 | * Handles actions to users. 18 | * 19 | * @param userDAO The user DAO implementation. 20 | */ 21 | class UserServiceInMemory extends UserService { 22 | 23 | /** 24 | * Create a user from login information and signup information 25 | * 26 | * @param loginInfo The information about login 27 | * @param signUp The information about User 28 | * @param avatarUrl string with url to avatar image 29 | * @param json all json with signup information 30 | */ 31 | def create(loginInfo: LoginInfo, signUp: SignUp, avatarUrl: Option[String] = None, json: JsValue = JsNull): Future[User] = { 32 | val fullName = signUp.fullName.getOrElse(signUp.firstName.getOrElse("None") + " " + signUp.lastName.getOrElse("None")) 33 | val info = BaseInfo( 34 | firstName = signUp.firstName, 35 | lastName = signUp.lastName, 36 | fullName = Some(fullName), 37 | gender = None) 38 | val user = User( 39 | loginInfo = loginInfo, 40 | email = Some(signUp.identifier), 41 | username = None, 42 | avatarUrl = avatarUrl, 43 | info = info) 44 | Future.successful { 45 | User( 46 | loginInfo = loginInfo, 47 | email = Some(signUp.identifier), 48 | username = None, 49 | avatarUrl = avatarUrl, 50 | info = info) 51 | } 52 | } 53 | 54 | /** 55 | * Retrieves a user that matches the specified login info. 56 | * 57 | * @param loginInfo The login info to retrieve a user. 58 | * @return The retrieved user or None if no user could be retrieved for the given login info. 59 | */ 60 | def retrieve(loginInfo: LoginInfo): Future[Option[User]] = { 61 | play.Logger.debug { 62 | s"""UserServiceImpl.retrieve ---------- 63 | ------------------ loginInfo: ${loginInfo} 64 | ------------------ DB: ${UserServiceImpl.users}""" 65 | } 66 | Future.successful { 67 | UserServiceImpl.users.find { 68 | case (id, user) => user.loginInfo == loginInfo || user.socials.map(_.find(li => li == loginInfo)).isDefined 69 | }.map(_._2) 70 | } 71 | } 72 | 73 | /** 74 | * Saves a user. 75 | * 76 | * @param user The user to save. 77 | * @return The saved user. 78 | */ 79 | def save(user: User) = { 80 | play.Logger.debug { 81 | s"""UserServiceImpl.save ---------- 82 | ------------------ user: ${user}""" 83 | } 84 | UserServiceImpl.users += (user.loginInfo.toString -> user) 85 | Future.successful(user) 86 | } 87 | 88 | /** 89 | * Saves the social profile for a user. 90 | * 91 | * If a user exists for this profile then update the user, otherwise create a new user with the given profile. 92 | * 93 | * @param profile The social profile to save. 94 | * @return The user for whom the profile was saved. 95 | */ 96 | def save[A <: AuthInfo](profile: CommonSocialProfile) = { 97 | play.Logger.debug { 98 | s"""UserServiceImpl.save ---------- 99 | ------------------ profile: ${profile}""" 100 | } 101 | retrieve(profile.loginInfo).flatMap { 102 | case Some(user) => // Update user with profile 103 | val u = user.copy(info = BaseInfo( 104 | firstName = profile.firstName, 105 | lastName = profile.lastName, 106 | fullName = profile.fullName, 107 | gender = None), 108 | email = profile.email, 109 | avatarUrl = profile.avatarURL) 110 | save(u) 111 | case None => // Insert a new user 112 | val u = User( 113 | loginInfo = profile.loginInfo, 114 | username = None, 115 | info = BaseInfo( 116 | firstName = profile.firstName, 117 | lastName = profile.lastName, 118 | fullName = profile.fullName, 119 | gender = None), 120 | email = profile.email, 121 | avatarUrl = profile.avatarURL) 122 | save(u) 123 | } 124 | } 125 | 126 | /** 127 | * Link a social social profile on a user. 128 | * 129 | * 130 | * @param profile The social profile to save. 131 | * @return The user for whom the profile was saved. 132 | */ 133 | def link[A <: AuthInfo](user: User, profile: CommonSocialProfile): Future[User] = { 134 | play.Logger.debug { 135 | s"""UserServiceImpl.link ---------- 136 | ------------------ user: ${user} 137 | ------------------ profile: ${profile} 138 | ------------------ DB: ${UserServiceImpl.users}""" 139 | } 140 | val s = user.socials.getOrElse(Seq()) 141 | val u = user.copy(socials = Some(s :+ profile.loginInfo)) 142 | save(u) 143 | } 144 | 145 | } 146 | 147 | object UserServiceImpl { 148 | val users: mutable.HashMap[String, User] = mutable.HashMap() 149 | } 150 | -------------------------------------------------------------------------------- /app/modules/cake/SocialProviderModule.scala: -------------------------------------------------------------------------------- 1 | package modules.cake 2 | 3 | import play.api.Play 4 | import play.api.Play.current 5 | import com.mohiva.play.silhouette.impl.providers.oauth2._ 6 | import com.mohiva.play.silhouette.impl.providers.oauth1._ 7 | import com.mohiva.play.silhouette.impl.providers.oauth1.secrets.{ CookieSecretProvider, CookieSecretSettings } 8 | import com.mohiva.play.silhouette.api.util.CacheLayer 9 | import com.mohiva.play.silhouette.api.util.HTTPLayer 10 | import com.mohiva.play.silhouette.api.util.Clock 11 | import com.mohiva.play.silhouette.api.util.IDGenerator 12 | import com.mohiva.play.silhouette.api.util.ExtractableRequest 13 | import com.mohiva.play.silhouette.impl.providers.{ OAuth1Settings, OAuth2Settings, OAuth1TokenSecretProvider } 14 | import com.mohiva.play.silhouette.impl.providers.OAuth2StateProvider 15 | import com.mohiva.play.silhouette.impl.providers.oauth1.services.PlayOAuth1Service 16 | import com.mohiva.play.silhouette.impl.providers.oauth2.state.{ CookieStateProvider, DummyStateProvider, CookieStateSettings } 17 | 18 | /** 19 | * Provide all social providers 20 | */ 21 | trait SocialProviderModule { 22 | 23 | def cacheLayer: CacheLayer 24 | def httpLayer: HTTPLayer 25 | def idGenerator: IDGenerator 26 | 27 | /** 28 | * Provides the OAuth2 state provider. 29 | * 30 | * @param idGenerator The ID generator implementation. 31 | * @return The OAuth2 state provider implementation. 32 | */ 33 | lazy val stateProvider: OAuth2StateProvider = new DummyStateProvider 34 | 35 | /** 36 | * Provides the OAuth1 token secret provider. 37 | * 38 | * @return The OAuth1 token secret provider implementation. 39 | */ 40 | lazy val oAuth1TokenSecretProvider: OAuth1TokenSecretProvider = { 41 | new CookieSecretProvider(CookieSecretSettings( 42 | cookieName = Play.configuration.getString("silhouette.oauth1TokenSecretProvider.cookieName").get, 43 | cookiePath = Play.configuration.getString("silhouette.oauth1TokenSecretProvider.cookiePath").get, 44 | cookieDomain = Play.configuration.getString("silhouette.oauth1TokenSecretProvider.cookieDomain"), 45 | secureCookie = Play.configuration.getBoolean("silhouette.oauth1TokenSecretProvider.secureCookie").get, 46 | httpOnlyCookie = Play.configuration.getBoolean("silhouette.oauth1TokenSecretProvider.httpOnlyCookie").get, 47 | expirationTime = Play.configuration.getInt("silhouette.oauth1TokenSecretProvider.expirationTime").get 48 | ), Clock()) 49 | } 50 | 51 | /** 52 | * Provides the Facebook provider. 53 | * 54 | * @param cacheLayer The cache layer implementation. 55 | * @param httpLayer The HTTP layer implementation. 56 | * @return The Facebook provider. 57 | */ 58 | lazy val facebookProvider = { 59 | FacebookProvider(httpLayer, stateProvider, OAuth2Settings( 60 | authorizationURL = Play.configuration.getString("silhouette.facebook.authorizationURL"), 61 | accessTokenURL = Play.configuration.getString("silhouette.facebook.accessTokenURL").get, 62 | redirectURL = Play.configuration.getString("silhouette.facebook.redirectURL").get, 63 | clientID = Play.configuration.getString("silhouette.facebook.clientID").get, 64 | clientSecret = Play.configuration.getString("silhouette.facebook.clientSecret").get, 65 | scope = Play.configuration.getString("silhouette.facebook.scope"))) 66 | } 67 | 68 | /** 69 | * Provides the Google provider. 70 | * 71 | * @param cacheLayer The cache layer implementation. 72 | * @param httpLayer The HTTP layer implementation. 73 | * @return The Google provider. 74 | */ 75 | lazy val provideGoogleProvider = { 76 | GoogleProvider(httpLayer, stateProvider, OAuth2Settings( 77 | authorizationURL = Play.configuration.getString("silhouette.google.authorizationURL"), 78 | accessTokenURL = Play.configuration.getString("silhouette.google.accessTokenURL").get, 79 | redirectURL = Play.configuration.getString("silhouette.google.redirectURL").get, 80 | clientID = Play.configuration.getString("silhouette.google.clientID").get, 81 | clientSecret = Play.configuration.getString("silhouette.google.clientSecret").get, 82 | scope = Play.configuration.getString("silhouette.google.scope"))) 83 | } 84 | 85 | /** 86 | * Provides the Twitter provider. 87 | * 88 | * @param cacheLayer The cache layer implementation. 89 | * @param httpLayer The HTTP layer implementation. 90 | * @return The Twitter provider. 91 | */ 92 | lazy val provideTwitterProvider = { 93 | val settings = OAuth1Settings( 94 | requestTokenURL = Play.configuration.getString("silhouette.twitter.requestTokenURL").get, 95 | accessTokenURL = Play.configuration.getString("silhouette.twitter.accessTokenURL").get, 96 | authorizationURL = Play.configuration.getString("silhouette.twitter.authorizationURL").get, 97 | callbackURL = Play.configuration.getString("silhouette.twitter.callbackURL").get, 98 | consumerKey = Play.configuration.getString("silhouette.twitter.consumerKey").get, 99 | consumerSecret = Play.configuration.getString("silhouette.twitter.consumerSecret").get) 100 | 101 | TwitterProvider(httpLayer, new PlayOAuth1Service(settings), oAuth1TokenSecretProvider, settings) 102 | } 103 | 104 | } --------------------------------------------------------------------------------