GoodSoft
  • Projekty
  • Zespół
  • Klienci
  • Technologie
  • Kariera
  • Blog
  • Kontakt
  • Hub of Talents
  • eu
  • Polski
    • Polski
    • English
    • Deutsch
Zaznacz stronę

Play Silhouette i Logowanie Społecznościowe

utworzone przez GoodSoft | sie 5, 2022 | Uncategorized | 0 komentarzy

Google Facebook Linkedin login implementation programming play silhouette

Play Silhouette i logowanie przez Sociale

Większość użytkowników nie zadaje sobie trudu, aby wypełnić formularz ze swoim e-mailem, imieniem i nazwiskiem, tym samym loginem co na każdej innej stronie i (najtrudniejsza część) dwoma identycznymi, skomplikowanymi i silnymi hasłami. Następnie za każdym razem, gdy wracają na Twoją stronę, muszą się zalogować, co oznacza przypominanie sobie, którego hasła użyli. Jednym ze sposobów uproszczenia tego jest autoryzacja użytkowników do Twojej aplikacji za pomocą loginów społecznościowych.

Play i Silhouette

Silhouette to popularna biblioteka uwierzytelniania Scala dla Play Framework. Obsługuje wiele metod uwierzytelniania – w tym OAuth2. Zawiera również wiele wstępnie skonfigurowanych klas Provider dla popularnych serwisów społecznościowych, co minimalizuje ilość niezbędnej pracy.

Konfiguracja

Pierwszą rzeczą, którą musimy skonfigurować jest dostawca state parameter. Będziemy go używać do zapobiegania atakom CSRF.

Pierwszym krokiem jest dodanie kilku kluczy konfiguracyjnych i wartości do silhouette.conf. Nasz snippet poniżej przedstawia konfigurację zarówno dla CSRF state handler jak i dla OAuth2 state provider – ponieważ będziemy potrzebować ich obu.

# OAuth2 state provider settings
oauth2StateProvider.cookieName="OAuth2TokenSecret"
oauth2StateProvider.cookiePath="/"
oauth2StateProvider.secureCookie=false
oauth2StateProvider.httpOnlyCookie=true
oauth2StateProvider.sameSite="Lax"
oauth2StateProvider.expirationTime=5 minutes
oauth2StateProvider.signer.key = "[changeme]"
oauth2StateProvider.crypter.key = "[changeme]"

# Social state handler
socialStateHandler.signer.key = "[changeme]"

# CSRF state item handler settings
csrfStateItemHandler.cookieName="OAuth2State"
csrfStateItemHandler.cookiePath="/"
csrfStateItemHandler.secureCookie=false
csrfStateItemHandler.httpOnlyCookie=true
csrfStateItemHandler.sameSite="Lax"
csrfStateItemHandler.expirationTime=5 minutes

csrfStateItemHandler.signer.key = "[changeme]"

Signery

Następną rzeczą, którą należy zrobić, jest stworzenie Signer f dla CSRF state item handler i OAuth2 state provider. Będą one używane do podpisywania ciasteczek po stronie klienta. Ta implementacja powinna wyglądać jak ta poniżej i powinna być umieszczona w tym samym pliku co reszta funkcji Silhouette np. SilhouetteModule.scala.

/**
 * Provides the signer for the social state handler.
 *
 * @param configuration The Play configuration.
 * @return The signer for the social state handler.
 */
@Provides @Named("social-state-signer")
def provideSocialStateSigner(configuration: Configuration): Signer = {
 val config = configuration.underlying.as[JcaSignerSettings]("silhouette.socialStateHandler.signer")

 new JcaSigner(config)
}
/**
 * Provides the signer for the CSRF state item handler.
 *
 * @param configuration The Play configuration.
 * @return The signer for the CSRF state item handler.
 */
@Provides @Named("csrf-state-item-signer")
def provideCSRFStateItemSigner(configuration: Configuration): Signer = {
 val config = configuration.underlying.as[JcaSignerSettings]("silhouette.csrfStateItemHandler.signer")

 new JcaSigner(config)
}

Dostawcy

Po utworzeniu Signer, możemy stworzyć zarówno CSRF state item handler jak i social state handler. Najpierw stworzymy CSRF item handler.

/**
 * Provides the CSRF state item handler.
 *
 * @param idGenerator The ID generator implementation.
 * @param signer The signer implementation.
 * @param configuration The Play configuration.
 * @return The CSRF state item implementation.
 */
@Provides
def provideCsrfStateItemHandler(
 idGenerator: IDGenerator,
 @Named("csrf-state-item-signer") signer: Signer,
 configuration: Configuration): CsrfStateItemHandler = {
 val settings = configuration.underlying.as[CsrfStateSettings]("silhouette.csrfStateItemHandler")
 new CsrfStateItemHandler(settings, idGenerator, signer)
}

Teraz używając tego CSRF state item handler stworzymy social state handler. Użyjemy DefaultSocialStateHandler ponieważ dokumentacja Silhouette sugeruje, że jest to najlepsza opcja.

/**
 * Provides the social state handler.
 *
 * @param signer The signer implementation.
 * @return The social state handler implementation.
 */
@Provides
def provideSocialStateHandler(
 @Named("social-state-signer") signer: Signer,
 csrfStateItemHandler: CsrfStateItemHandler): SocialStateHandler = {

 new DefaultSocialStateHandler(Set(csrfStateItemHandler), signer)
}

Dostawcy mediów społecznościowych

Teraz, gdy skonfigurowaliśmy Silhouette dla przepływu OAuth2 i zabezpieczyliśmy go przed atakami CSRF, możemy stworzyć naszych dostawców serwisów społecznościowych. Na potrzeby tego artykułu zaimplementujemy tylko dostawcę Google, ale inne konfiguracje są bardzo podobne. Możesz znaleźć przykładowe konfiguracje w dokumentacji Silhouette. Najprawdopodobniej będziesz chciał używać więcej niż jednego providera dla wygody swoich użytkowników. Kod przedstawiony w tej części artykułu będzie łatwo rozszerzalny.

Konfiguracja dostawcy

Pierwszą rzeczą, którą należy zrobić jest dodanie konfiguracji dostawcy do pliku silhouette.conf. Dla Google powinien on wyglądać podobnie jak ten poniżej.

google {
     authorizationURL="https://accounts.google.com/o/oauth2/auth"
     accessTokenURL="https://accounts.google.com/o/oauth2/token"
     redirectURL=YOUR_APP_DOMAIN/authenticate/google"
     clientID="some_client_id"
     clientSecret="some_client_secret"
     scope="profile email"
   }

Uwaga: Musisz wygenerować clientID i clientSecret na swoim koncie Google.

Następnym krokiem jest stworzenie GoogleProvider. Ponownie zrobimy to w klasie SilhouetteModule. Poniższy snippet kodu przedstawia implementację, która wykorzysta konfigurację zapisaną w silhouette.conf.

/**
 * Provides the Google provider.
 *
 * @param httpLayer The HTTP layer implementation.
 * @param socialStateHandler The social state handler implementation.
 * @param configuration The Play configuration.
 * @return The Google provider.
 */
@Provides
def provideGoogleProvider(
 httpLayer: HTTPLayer,
 socialStateHandler: SocialStateHandler,
 configuration: Configuration): GoogleProvider = {

 new GoogleProvider(httpLayer, socialStateHandler, configuration.underlying.as[OAuth2Settings]("silhouette.google"))
}

Teraz moglibyśmy użyć tylko tego providera w dalszej implementacji, ale dla łatwości rozszerzenia stworzymy SocialProviderRegistry. Pozwoli nam to na łatwe dodawanie kolejnych providerów w przyszłości bez konieczności zmiany implementacji innych klas. Rejestr ten możemy stworzyć w następujący sposób:

/**
 * Provides the social provider registry.
 *
 * @param facebookProvider The Facebook provider implementation.
 * @param googleProvider The Google provider implementation.
 * @param linkedInProvider The LinkedIn provider implementation.
 * @return The Silhouette environment.
 */
@Provides
def provideSocialProviderRegistry(
 googleProvider: GoogleProvider
 // add more providers here
 ): SocialProviderRegistry = {

 SocialProviderRegistry(Seq(
   googleProvider
   // add more providers here
 )) }

Kontroler uwierzytelniania

Teraz, gdy mamy już stworzonych providerów, musimy stworzyć punkt końcowy do uwierzytelniania. Najpierw, aby uzyskać dostęp do naszego repozytorium, musimy wstrzyknąć je do kontrolera, poprzez dodanie @Inject()(socialProviderRegistry: SocialProviderRegistry) do klasy kontrolera. Kolejnym krokiem jest stworzenie Action która będzie logować użytkowników, na podstawie danych przesłanych przez providera. Poniższa metoda pobiera ciąg provider aby określić, który social provider został użyty przez naszego użytkownika i użyć tego właściwego. Następnie spróbuje uwierzytelnić użytkownika w oparciu o użycie wcześniej ustalonego providera. Jeśli zakończy się ona sukcesem (Right), pobierze dane z profilu przesłane przez dostawcę społecznościowego i zapisze je w bazie danych (userService.save(profile) jest niestandardową funkcją, którą musisz stworzyć sam). Jeśli serwis zwróci Some(user) co w naszej implementacji oznacza, że dane zostały poprawnie zapisane, utworzy authenticator i opublikuje LoginEvent skutecznie logując nowego użytkownika.

/**
 * Social log in
 *
 * @param provider login provider
 */
def socialAuthenticate(provider: String) = silhouette.UnsecuredAction.async { implicit request =>
 (socialProviderRegistry.get[SocialProvider](provider) match {

   case Some(p: SocialProvider with CommonSocialProfileBuilder) =>
     p.authenticate().flatMap {

       case Left(result) => Future.successful(result) // Return authentication result with error

       case Right(authInfo) => (for {
         profile <- p.retrieveProfile(authInfo)
         optionalUser <- userService.save(profile)        } yield optionalUser match {          case Some(user) =>
           for {
             authenticator <- silhouette.env.authenticatorService.create(user.loginInfo)
             value <- silhouette.env.authenticatorService.init(authenticator)
             result <- silhouette.env.authenticatorService.embed(value, Redirect(routes.HomeController.index()))            } yield {              silhouette.env.eventBus.publish(LoginEvent(user, request))              result            }          case None => // Error handling while saving user
           Future.successful(Redirect(routes.AuthController.login).flashing("warning" -> Messages("auth.social.alreadyRegistered")))

       }).flatten

     }

   case _ => Future.failed(new ProviderException(s"Cannot authenticate with unexpected social provider $provider")) // Wrong provider key

 }).recover { // Any other unforeseen error handling
   case e: ProviderException =>
     logger.error("Unexpected provider error", e)
     Redirect(routes.AuthController.login).flashing("warning" -> Messages("auth.social.couldNotAuthenticate"))
 }
}

Jak każdy endpoint musimy dodać go do konfiguracji routes . Powinna ona wyglądać podobnie do tej:

GET   /authenticate/:provider  controllers.AuthController.socialAuthenticate(provider: String)

Szablon

Ostatnią rzeczą, którą należy zrobić jest umożliwienie użytkownikom dostępu do tej metody logowania. Zrobimy to dodając ten snippet do szablonu logowania.

@if(socialProviders.providers.nonEmpty) {
  <div class="social-providers mt-2">
     <p class="col-sm-12 justify-content-center d-flex">@messages("auth.signIn.useSocial")</p>
     <div class="col-sm-12 justify-content-center d-flex">
        @for(p <- socialProviders.providers) {       
           <a href="@controllers.routes.AuthController.socialAuthenticate(p.id)" class="social-auth provider @p.id" title="@messages(p.id)"><i aria-hidden="true" class="fab fa-@{p.id} font-50 m-2"></i></a>
        }
     </div>
  </div>
}

To stworzy linki do wszystkich dostawców społecznościowych z naszego Repozytorium. class="fab fa-@{p.id} font-50 m-2" pobierze ikony dostawców z FontAwesome.

Poland, Białystok ul. Żurawia 71

+48 512 098 660

contact@goodsoft.pl

  • Obserwuj
  • Obserwuj

Project covered

GoodSoft company is implementing a project co-financed by the European Funds from the Operational Programme Eastern Poland Priority axis 1 Entrepreneurial Eastern Poland measure 1.2 Internationalization of SMEs entitled ” Internationalization as an opportunity for the development of GoodSoft Michał Jurczuk company”, project number POPW .01.02.00-20-0026/20

Privacy policy

This website uses cookies for profiling purposes. Please read our cookie policy and agree to the actions taken. I accept cookies.

See more

Projects
Our Team
Clients
Technologies
Careers
Blog
Contact

Goodsoft | ul. Żurawia 71, 15-540 Białystok, Polska | NIP: 603 002 63 26 | Regon: 200 66 70 57