OAuth

note

OAuth - 图1

This help topic is in development and will be updated in the future.

OAuth defines a mechanism for authentication using external providers like Google or Facebook safely. You can read more about OAuth. Ktor has a feature to work with OAuth 1a and 2.0

A simplified OAuth 2.0 workflow:

  • The client is redirected to an authorize URL for the specified provider (Google, Facebook, Twitter, Github…). specifying the clientId and a valid redirection URL.

  • Once the login is correct, the provider generates an auth token using a clientSecret associated with that clientId.

  • Then the client is redirected to a valid, previously agreed upon, application URL with an auth token that is signed with the clientSecret.

  • Ktor’s OAuth feature verifies the token and generates a Principal OAuthAccessTokenResponse.

  • With the auth token, you can request, for example, the user’s email or id depending on the provider.

Example:

  1. @Location("/login/{type?}") class login(val type: String = "")
  2. val loginProviders = listOf(
  3. OAuthServerSettings.OAuth2ServerSettings(
  4. name = "github",
  5. authorizeUrl = "https://github.com/login/oauth/authorize",
  6. accessTokenUrl = "https://github.com/login/oauth/access_token",
  7. clientId = "***",
  8. clientSecret = "***"
  9. )
  10. ).associateBy {it.name}
  11. install(Authentication) {
  12. oauth("gitHubOAuth") {
  13. client = HttpClient(Apache)
  14. providerLookup = { loginProviders[application.locations.resolve<login>(login::class, this).type] }
  15. urlProvider = { url(login(it.name)) }
  16. }
  17. }
  18. routing {
  19. authenticate("gitHubOAuth") {
  20. location<login>() {
  21. param("error") {
  22. handle {
  23. call.loginFailedPage(call.parameters.getAll("error").orEmpty())
  24. }
  25. }
  26. handle {
  27. val principal = call.authentication.principal<OAuthAccessTokenResponse>()
  28. if (principal != null) {
  29. call.loggedInSuccessResponse(principal)
  30. } else {
  31. call.loginPage()
  32. }
  33. }
  34. }
  35. }
  36. }

Depending on the OAuth version, you will get a different Principal

  1. sealed class OAuthAccessTokenResponse : Principal {
  2. data class OAuth1a(
  3. val token: String, val tokenSecret: String,
  4. val extraParameters: Parameters = Parameters.Empty
  5. ) : OAuthAccessTokenResponse()
  6. data class OAuth2(
  7. val accessToken: String, val tokenType: String,
  8. val expiresIn: Long, val refreshToken: String?,
  9. val extraParameters: Parameters = Parameters.Empty
  10. ) : OAuthAccessTokenResponse()
  11. }

Guide, example and testing