|
class LoginController @Inject() ( |
|
// ... |
|
socialProviderRegistry: SocialProviderRegistry, |
|
configuration: Configuration, |
|
// ... |
|
) extends Silhouette[User, JWTAuthenticator] { |
|
|
|
private val log = Logger(this.getClass) |
|
|
|
// Fetch the email from the GitHub REST API |
|
private def fetchEmail(socialUid: String, provider: SocialProvider, a: AuthInfo): Future[Option[String]] = { |
|
log.debug(s"Could not find any email for $socialUid in result. Going to looking up using the provider REST API") |
|
val maybeUrl = configuration.getString(s"silhouette.${provider.id}.emailsURL") |
|
maybeUrl.map(u => |
|
provider match { |
|
case gh: GitHubProvider => |
|
log.debug(s"Trying to fetch a emails for $socialUid from GitHub.") |
|
wsClient.url(u.format(a.asInstanceOf[OAuth2Info].accessToken)).get() |
|
.map { response => |
|
val emails: Seq[GitHubEmail] = response.json.asOpt[Seq[GitHubEmail]].getOrElse(Seq.empty[GitHubEmail]) |
|
emails.find(_.primary).map(_.email) |
|
} |
|
.recover { |
|
case err: Exception => |
|
log.warn(s"There was an error fetching emails for $socialUid from GitHub.") |
|
None |
|
} |
|
case _ => |
|
Future.successful(None) |
|
}).getOrElse(Future.successful(None)) |
|
} |
|
|
|
// The authentication endpoint |
|
def authenticate(provider: String) = Action.async { implicit request => |
|
(socialProviderRegistry.get[SocialProvider](provider) match { |
|
case Some(p: SocialProvider with CommonSocialProfileBuilder) => |
|
p.authenticate().flatMap { |
|
case Left(result) => Future.successful(result) |
|
case Right(authInfo) => |
|
for { |
|
profile <- p.retrieveProfile(authInfo) |
|
maybeEmail <- if (profile.email.nonEmpty) Future.successful(profile.email) else fetchEmail(p.id, p, authInfo) |
|
user <- fromSocialProfile(profile.copy(email = maybeEmail)) |
|
successOrFailure <- Future.successful(userService.save(user)) |
|
authInfo <- authInfoRepository.save(profile.loginInfo, authInfo) |
|
authenticator <- env.authenticatorService.create(profile.loginInfo) |
|
value <- env.authenticatorService.init(authenticator) |
|
} yield { |
|
env.eventBus.publish(LoginEvent(user, request, request2Messages)) |
|
Ok(Json.obj( |
|
"token" -> value, |
|
"expiresOn" -> Json.toJson[DateTime](authenticator.expirationDateTime) |
|
)) |
|
} |
|
} |
|
case _ => |
|
Future.failed(new ProviderException(s"Social provider $provider is not supported")) |
|
}).recover { |
|
case e: ProviderException => |
|
log.error("Unexpected provider error", e) |
|
Unauthorized(Json.obj("msg" -> e.getMessage)) |
|
} |
|
} |
|
|
|
private def fromSocialProfile(prof: CommonSocialProfile): Future[User] = |
|
Future.successful { |
|
val maybeUser = userService.findByUsername(Username(prof.loginInfo.providerKey)) |
|
User.updateFromCommonSocialProfile(prof, maybeUser) |
|
} |
|
} |