CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-server-auth-jvm

Authentication and authorization plugin for Ktor server applications

Pending
Overview
Eval results
Files

oauth.mddocs/

OAuth Authentication

OAuth 1.0a and OAuth 2.0 authentication support for integrating with third-party authentication providers like Google, GitHub, Twitter, and custom OAuth servers. This includes authorization code flows, token validation, and user profile retrieval.

OAuth 2.0 Authentication

OAuth 2.0 authorization code flow implementation for modern OAuth providers.

Configuration

fun AuthenticationConfig.oauth(
    name: String? = null,
    configure: OAuthAuthenticationProvider.Config.() -> Unit
)

@KtorDsl
class OAuthAuthenticationProvider.Config(name: String?) : AuthenticationProvider.Config(name) {
    lateinit var client: HttpClient
    lateinit var providerLookup: ApplicationCall.() -> OAuthServerSettings?
    lateinit var urlProvider: ApplicationCall.(OAuthServerSettings) -> String
    fun skipWhen(predicate: ApplicationCallPredicate)
}

OAuth Version Enum

enum class OAuthVersion {
    V10a, V20
}

OAuth Server Settings

sealed class OAuthServerSettings {
    data class OAuth1aServerSettings(
        val name: String,
        val requestTokenUrl: String,
        val authorizeUrl: String,
        val accessTokenUrl: String,
        val consumerKey: String,
        val consumerSecret: String
    ) : OAuthServerSettings()
    
    data class OAuth2ServerSettings(
        val name: String,
        val authorizeUrl: String,
        val accessTokenUrl: String,
        val requestMethod: HttpMethod = HttpMethod.Post,
        val clientId: String,
        val clientSecret: String,
        val defaultScopes: List<String> = emptyList(),
        val extraAuthParameters: List<Pair<String, String>> = emptyList(),
        val extraTokenParameters: List<Pair<String, String>> = emptyList(),
        val accessTokenRequiresBasicAuth: Boolean = false,
        val onStateCreated: ((call: ApplicationCall, state: String) -> Unit)? = null,
        val authorizeUrlInterceptor: ((URLBuilder, ApplicationCall) -> Unit)? = null,
        val passParamsInURL: Boolean = false,
        val nonceManager: NonceManager = GenerateOnlyNonceManager
    ) : OAuthServerSettings()
}

Basic OAuth 2.0 Setup

val googleOAuthProvider = OAuthServerSettings.OAuth2ServerSettings(
    name = "google",
    authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
    accessTokenUrl = "https://www.googleapis.com/oauth2/v4/token",
    clientId = "your-google-client-id",
    clientSecret = "your-google-client-secret",
    defaultScopes = listOf("openid", "profile", "email")
)

install(Authentication) {
    oauth("auth-oauth-google") {
        client = HttpClient(Apache)
        providerLookup = { googleOAuthProvider }
        urlProvider = { url ->
            redirectUrl(url, "/callback")
        }
    }
}

routing {
    authenticate("auth-oauth-google") {
        get("/login") {
            // Redirects to OAuth provider
        }
        
        get("/callback") {
            val principal = call.principal<OAuthAccessTokenResponse.OAuth2>()
            if (principal != null) {
                // Use access token to get user info
                val userInfo = getUserInfo(principal.accessToken)
                call.sessions.set(UserSession(userInfo.id))
                call.respondRedirect("/dashboard")
            } else {
                call.respondRedirect("/login?error=oauth")
            }
        }
    }
}

OAuth 1.0a Authentication (JVM Only)

OAuth 1.0a implementation for legacy providers like Twitter API v1.1.

OAuth 1.0a Server Settings

data class OAuth1aServerSettings(
    val name: String,
    val requestTokenUrl: String,
    val authorizeUrl: String,
    val accessTokenUrl: String,
    val consumerKey: String,
    val consumerSecret: String
) : OAuthServerSettings()

OAuth 1.0a Setup

val twitterOAuthProvider = OAuthServerSettings.OAuth1aServerSettings(
    name = "twitter",
    requestTokenUrl = "https://api.twitter.com/oauth/request_token",
    authorizeUrl = "https://api.twitter.com/oauth/authorize",
    accessTokenUrl = "https://api.twitter.com/oauth/access_token",
    consumerKey = "your-twitter-consumer-key",
    consumerSecret = "your-twitter-consumer-secret"
)

install(Authentication) {
    oauth("auth-oauth-twitter") {
        client = HttpClient(Apache)
        providerLookup = { twitterOAuthProvider }
        urlProvider = { url ->
            redirectUrl(url, "/twitter-callback")
        }
    }
}

OAuth Callback Types

sealed class OAuthCallback {
    data class TokenSingle(val token: String, val state: String) : OAuthCallback()
    data class TokenPair(val token: String, val tokenSecret: String) : OAuthCallback()
    data class Error(val error: String, val errorDescription: String?) : OAuthCallback()
}

OAuth Access Token Responses

sealed class OAuthAccessTokenResponse {
    data class OAuth2(
        val accessToken: String,
        val tokenType: String,
        val expiresIn: Long?,
        val refreshToken: String?,
        val extraParameters: Parameters = Parameters.Empty
    ) : OAuthAccessTokenResponse()
    
    data class OAuth1a(
        val token: String,
        val tokenSecret: String,
        val extraParameters: Parameters = Parameters.Empty
    ) : OAuthAccessTokenResponse()
}

Advanced OAuth 2.0 Patterns

Custom State Management

oauth("custom-state") {
    client = HttpClient(CIO)
    providerLookup = { 
        googleOAuthProvider.copy(
            onStateCreated = { call, state ->
                // Store state with additional context
                call.sessions.set(OAuthState(state, call.request.uri))
            }
        )
    }
    urlProvider = { url -> redirectUrl(url, "/callback") }
}

Multiple OAuth Providers

install(Authentication) {
    oauth("google") {
        client = httpClient
        providerLookup = { googleProvider }
        urlProvider = { redirectUrl(it, "/auth/google/callback") }
    }
    
    oauth("github") {
        client = httpClient
        providerLookup = { githubProvider }
        urlProvider = { redirectUrl(it, "/auth/github/callback") }
    }
}

routing {
    get("/auth/{provider}") {
        val provider = call.parameters["provider"]
        authenticate(provider) {
            // Redirect to OAuth provider
        }
    }
}

Token Refresh Implementation

suspend fun refreshOAuth2Token(refreshToken: String): OAuthAccessTokenResponse.OAuth2? {
    return try {
        httpClient.submitForm(
            url = "https://oauth2.googleapis.com/token",
            formParameters = parameters {
                append("grant_type", "refresh_token")
                append("refresh_token", refreshToken)
                append("client_id", clientId)
                append("client_secret", clientSecret)
            }
        ).body()
    } catch (e: Exception) {
        null
    }
}

OAuth 2.0 Verification

suspend fun verifyWithOAuth2(
    client: HttpClient,
    settings: OAuthServerSettings.OAuth2ServerSettings,
    callbackResponse: OAuthCallback.TokenSingle,
    configure: HttpRequestBuilder.() -> Unit = {}
): OAuthAccessTokenResponse.OAuth2

suspend fun verifyWithOAuth2(
    credential: UserPasswordCredential,
    client: HttpClient,
    settings: OAuthServerSettings.OAuth2ServerSettings
): OAuthAccessTokenResponse.OAuth2

Resource Owner Password Flow

suspend fun authenticateWithResourceOwner(
    username: String,
    password: String
): OAuthAccessTokenResponse.OAuth2? {
    return verifyWithOAuth2(
        client = httpClient,
        settings = oauth2Settings,
        callbackResponse = OAuthCallback.TokenSingle(
            token = "", // Not used in resource owner flow
            state = ""
        )
    ) {
        parameter("grant_type", "password")
        parameter("username", username)
        parameter("password", password)
    }
}

OAuth Exceptions

sealed class OAuth2Exception : Exception() {
    class InvalidGrant(message: String) : OAuth2Exception()
    class InvalidNonce(message: String) : OAuth2Exception()
    class MissingAccessToken(message: String) : OAuth2Exception()
    class UnsupportedGrantType(message: String) : OAuth2Exception()
    class UnknownException(message: String, cause: Throwable?) : OAuth2Exception()
}

sealed class OAuth1aException : Exception() {
    class MissingTokenException(message: String) : OAuth1aException()
}

Common OAuth Grant Types

object OAuthGrantTypes {
    const val AuthorizationCode = "authorization_code"
    const val RefreshToken = "refresh_token"
    const val Password = "password"
    const val ClientCredentials = "client_credentials"
}

OAuth Request/Response Parameters

object OAuth2RequestParameters {
    const val ClientId = "client_id"
    const val ClientSecret = "client_secret"
    const val Code = "code"
    const val GrantType = "grant_type"
    const val RedirectUri = "redirect_uri"
    const val ResponseType = "response_type"
    const val Scope = "scope"
    const val State = "state"
}

object OAuth2ResponseParameters {
    const val AccessToken = "access_token"
    const val TokenType = "token_type"
    const val ExpiresIn = "expires_in"
    const val RefreshToken = "refresh_token"
    const val Scope = "scope"
    const val State = "state"
    const val Error = "error"
    const val ErrorDescription = "error_description"
}

Platform Availability

  • OAuth 2.0: Available on all platforms (common, JVM, JS/WASM, native)
  • OAuth 1.0a: JVM only due to cryptographic signature requirements
  • Full OAuth procedure support: JVM only

Security Considerations

OAuth 2.0 Security

  • Always use HTTPS for OAuth flows
  • Validate state parameter to prevent CSRF attacks
  • Store client secrets securely (environment variables, secure vaults)
  • Use PKCE (Proof Key for Code Exchange) for public clients
  • Implement proper token storage and transmission
  • Validate redirect URIs to prevent authorization code interception
  • Use short-lived access tokens with refresh tokens when possible

OAuth 1.0a Security

  • Implement proper signature verification
  • Use secure random nonce generation
  • Protect consumer secrets and token secrets
  • Implement replay attack prevention
  • Validate all OAuth 1.0a signature components

General OAuth Security

  • Implement proper error handling without information leakage
  • Log OAuth events for security monitoring
  • Use minimal required scopes
  • Implement token revocation when needed
  • Validate JWT tokens properly if using OpenID Connect

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-server-auth-jvm

docs

basic-auth.md

bearer-auth.md

form-session-auth.md

index.md

jvm-features.md

oauth.md

tile.json