feat: v2-alpha (#112)
There are still some bugs, still some outstanding features, but I don't want to hold this back any longer, that way I can keep the future PRs much more focused
This commit is contained in:
9
kompendium-auth/Module.md
Normal file
9
kompendium-auth/Module.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Module kompendium-auth
|
||||
|
||||
This module is responsible for providing wrappers around ktor-auth configuration blocks, allowing users to document
|
||||
their API authentication with minimal modifications to their existing configuration.
|
||||
|
||||
# Package io.bkbn.kompendium.auth
|
||||
|
||||
Base package that is responsible for setting up required authentication route handlers along with exposing
|
||||
wrapper methods for each ktor-auth authentication mechanism.
|
@ -1,78 +1,18 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("io.bkbn.sourdough.library")
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation(libs.bundles.ktor)
|
||||
implementation(libs.bundles.ktorAuth)
|
||||
// IMPLEMENTATION
|
||||
|
||||
val ktorVersion: String by project
|
||||
implementation(projects.kompendiumCore)
|
||||
implementation(group = "io.ktor", name = "ktor-server-core", version = ktorVersion)
|
||||
implementation(group = "io.ktor", name = "ktor-auth", version = ktorVersion)
|
||||
implementation(group = "io.ktor", name = "ktor-auth-jwt", version = ktorVersion)
|
||||
|
||||
testImplementation(libs.ktor.jackson)
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||
testImplementation(libs.jackson.module.kotlin)
|
||||
testImplementation(libs.ktor.server.test.host)
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GithubPackages"
|
||||
url = uri("https://maven.pkg.github.com/bkbnio/kompendium")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("kompendium") {
|
||||
from(components["kotlin"])
|
||||
artifact(tasks.sourcesJar)
|
||||
artifact(tasks.javadocJar)
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name.toLowerCase()
|
||||
version = project.version.toString()
|
||||
|
||||
pom {
|
||||
name.set("Kompendium")
|
||||
description.set("A minimally invasive OpenAPI spec generator for Ktor")
|
||||
url.set("https://github.com/bkbnio/Kompendium")
|
||||
licenses {
|
||||
license {
|
||||
name.set("MIT License")
|
||||
url.set("https://mit-license.org/")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("bkbnio")
|
||||
name.set("Ryan Brink")
|
||||
email.set("admin@bkbn.io")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:git://github.com/bkbnio/Kompendium.git")
|
||||
developerConnection.set("scm:git:ssh://github.com/bkbnio/Kompendium.git")
|
||||
url.set("https://github.com/bkbnio/Kompendium.git")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
sign(publishing.publications)
|
||||
// TESTING
|
||||
|
||||
testImplementation(testFixtures(projects.kompendiumCore))
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
package io.bkbn.kompendium.auth
|
||||
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.basic
|
||||
import io.ktor.auth.BasicAuthenticationProvider
|
||||
import io.ktor.auth.jwt.jwt
|
||||
import io.ktor.auth.jwt.JWTAuthenticationProvider
|
||||
import io.bkbn.kompendium.Kompendium
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecSchemaSecurity
|
||||
import io.ktor.auth.AuthenticationRouteSelector
|
||||
|
||||
object KompendiumAuth {
|
||||
|
||||
init {
|
||||
Kompendium.addCustomRouteHandler(AuthenticationRouteSelector::class) { route, tail ->
|
||||
calculate(route.parent, tail)
|
||||
}
|
||||
}
|
||||
|
||||
fun Authentication.Configuration.notarizedBasic(
|
||||
name: String? = null,
|
||||
configure: BasicAuthenticationProvider.Configuration.() -> Unit
|
||||
) {
|
||||
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
|
||||
type = "http",
|
||||
scheme = "basic"
|
||||
)
|
||||
basic(name, configure)
|
||||
}
|
||||
|
||||
fun Authentication.Configuration.notarizedJwt(
|
||||
name: String? = null,
|
||||
header: String? = null,
|
||||
scheme: String? = null,
|
||||
configure: JWTAuthenticationProvider.Configuration.() -> Unit
|
||||
) {
|
||||
if (header == null || header == "Authorization") {
|
||||
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
|
||||
type = "http",
|
||||
scheme = scheme ?: "bearer"
|
||||
)
|
||||
} else {
|
||||
Kompendium.openApiSpec.components.securitySchemes[name ?: "default"] = OpenApiSpecSchemaSecurity(
|
||||
type = "apiKey",
|
||||
name = header,
|
||||
`in` = "header"
|
||||
)
|
||||
}
|
||||
jwt(name, configure)
|
||||
}
|
||||
|
||||
// TODO support other authentication providers (e.g., oAuth)?
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.bkbn.kompendium.auth
|
||||
|
||||
import io.bkbn.kompendium.auth.configuration.ApiKeyConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.BasicAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.JwtAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.OAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.SecurityConfiguration
|
||||
import io.bkbn.kompendium.core.Kompendium
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
import io.bkbn.kompendium.oas.security.BasicAuth
|
||||
import io.bkbn.kompendium.oas.security.BearerAuth
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.ktor.application.feature
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.application
|
||||
|
||||
object Notarized {
|
||||
|
||||
fun Route.notarizedAuthenticate(
|
||||
vararg configurations: SecurityConfiguration,
|
||||
optional: Boolean = false,
|
||||
build: Route.() -> Unit
|
||||
): Route {
|
||||
val configurationNames = configurations.map { it.name }.toTypedArray()
|
||||
val feature = application.feature(Kompendium)
|
||||
|
||||
configurations.forEach { config ->
|
||||
feature.config.spec.components.securitySchemes[config.name] = when (config) {
|
||||
is ApiKeyConfiguration -> ApiKeyAuth(config.location, config.keyName)
|
||||
is BasicAuthConfiguration -> BasicAuth()
|
||||
is JwtAuthConfiguration -> BearerAuth(config.bearerFormat)
|
||||
is OAuthConfiguration -> OAuth(config.description, config.flows)
|
||||
}
|
||||
}
|
||||
|
||||
return authenticate(*configurationNames, optional = optional, build = build)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.bkbn.kompendium.auth.configuration
|
||||
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
|
||||
interface ApiKeyConfiguration : SecurityConfiguration {
|
||||
val location: ApiKeyAuth.ApiKeyLocation
|
||||
val keyName: String
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package io.bkbn.kompendium.auth.configuration
|
||||
|
||||
interface BasicAuthConfiguration : SecurityConfiguration
|
@ -0,0 +1,6 @@
|
||||
package io.bkbn.kompendium.auth.configuration
|
||||
|
||||
interface JwtAuthConfiguration : SecurityConfiguration {
|
||||
val bearerFormat: String
|
||||
get() = "JWT"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.bkbn.kompendium.auth.configuration
|
||||
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
|
||||
interface OAuthConfiguration: SecurityConfiguration {
|
||||
val flows: OAuth.Flows
|
||||
val description: String?
|
||||
get() = null
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package io.bkbn.kompendium.auth.configuration
|
||||
|
||||
sealed interface SecurityConfiguration {
|
||||
val name: String
|
||||
}
|
@ -1,201 +1,69 @@
|
||||
package io.bkbn.kompendium.auth
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.jackson.jackson
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.routing.routing
|
||||
import io.ktor.server.testing.handleRequest
|
||||
import io.ktor.server.testing.withTestApplication
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import io.bkbn.kompendium.Kompendium
|
||||
import io.bkbn.kompendium.Notarized.notarizedGet
|
||||
import io.bkbn.kompendium.auth.KompendiumAuth.notarizedBasic
|
||||
import io.bkbn.kompendium.auth.KompendiumAuth.notarizedJwt
|
||||
import io.bkbn.kompendium.auth.util.TestData
|
||||
import io.bkbn.kompendium.auth.util.TestParams
|
||||
import io.bkbn.kompendium.auth.util.TestResponse
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo
|
||||
import io.bkbn.kompendium.models.meta.ResponseInfo
|
||||
import io.bkbn.kompendium.routes.openApi
|
||||
import io.bkbn.kompendium.routes.redoc
|
||||
import io.bkbn.kompendium.auth.configuration.BasicAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.JwtAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.configuration.OAuthConfiguration
|
||||
import io.bkbn.kompendium.auth.util.AuthConfigName
|
||||
import io.bkbn.kompendium.auth.util.configBasicAuth
|
||||
import io.bkbn.kompendium.auth.util.configJwtAuth
|
||||
import io.bkbn.kompendium.auth.util.notarizedAuthRoute
|
||||
import io.bkbn.kompendium.auth.util.setupOauth
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTest
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
internal class KompendiumAuthTest {
|
||||
|
||||
@AfterTest
|
||||
fun `reset kompendium`() {
|
||||
Kompendium.resetSchema()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get with basic authentication records all expected information`() {
|
||||
withTestApplication({
|
||||
configModule()
|
||||
configBasicAuth()
|
||||
docs()
|
||||
notarizedAuthenticatedGetModule(TestData.AuthConfigName.Basic)
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_basic_authenticated_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get with jwt authentication records all expected information`() {
|
||||
withTestApplication({
|
||||
configModule()
|
||||
configJwtAuth()
|
||||
docs()
|
||||
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_jwt_authenticated_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get with jwt authentication and custom scheme records all expected information`() {
|
||||
withTestApplication({
|
||||
configModule()
|
||||
configJwtAuth(scheme = "oauth")
|
||||
docs()
|
||||
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_jwt_custom_scheme_authenticated_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get with jwt authentication and custom header records all expected information`() {
|
||||
withTestApplication({
|
||||
configModule()
|
||||
configJwtAuth(header = "x-api-key")
|
||||
docs()
|
||||
notarizedAuthenticatedGetModule(TestData.AuthConfigName.JWT)
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_jwt_custom_header_authenticated_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get with multiple jwt schemes records all expected information`() {
|
||||
withTestApplication({
|
||||
configModule()
|
||||
install(Authentication) {
|
||||
notarizedJwt("jwt1", header = "x-api-key-1") {
|
||||
realm = "Ktor server"
|
||||
}
|
||||
notarizedJwt("jwt2", header = "x-api-key-2") {
|
||||
realm = "Ktor server"
|
||||
}
|
||||
class KompendiumAuthTest : DescribeSpec({
|
||||
describe("Basic Authentication") {
|
||||
it("Can create a notarized basic authentication record with all expected information") {
|
||||
// arrange
|
||||
val authConfig = object : BasicAuthConfiguration {
|
||||
override val name: String = AuthConfigName.Basic
|
||||
}
|
||||
docs()
|
||||
notarizedAuthenticatedGetModule("jwt1", "jwt2")
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_multiple_jwt_authenticated_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.configModule() {
|
||||
install(ContentNegotiation) {
|
||||
jackson(ContentType.Application.Json) {
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
// act
|
||||
openApiTest("notarized_basic_authenticated_get.json") {
|
||||
configBasicAuth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
describe("JWT Authentication") {
|
||||
it("Can create a simple notarized JWT route") {
|
||||
// arrange
|
||||
val authConfig = object : JwtAuthConfiguration {
|
||||
override val name: String = AuthConfigName.JWT
|
||||
}
|
||||
|
||||
private fun Application.configBasicAuth() {
|
||||
install(Authentication) {
|
||||
notarizedBasic(TestData.AuthConfigName.Basic) {
|
||||
realm = "Ktor Server"
|
||||
validate { credentials ->
|
||||
if (credentials.name == credentials.password) {
|
||||
UserIdPrincipal(credentials.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
// act
|
||||
openApiTest("notarized_jwt_authenticated_get.json") {
|
||||
configJwtAuth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.configJwtAuth(
|
||||
header: String? = null,
|
||||
scheme: String? = null
|
||||
) {
|
||||
install(Authentication) {
|
||||
notarizedJwt(TestData.AuthConfigName.JWT, header, scheme) {
|
||||
realm = "Ktor server"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.notarizedAuthenticatedGetModule(vararg authenticationConfigName: String) {
|
||||
routing {
|
||||
authenticate(*authenticationConfigName) {
|
||||
route(TestData.getRoutePath) {
|
||||
notarizedGet(testGetInfo(*authenticationConfigName)) {
|
||||
call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val oas = Kompendium.openApiSpec.copy()
|
||||
|
||||
private fun Application.docs() {
|
||||
routing {
|
||||
openApi(oas)
|
||||
redoc(oas)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val testGetResponse = ResponseInfo<TestResponse>(HttpStatusCode.OK, "A Successful Endeavor")
|
||||
fun testGetInfo(vararg security: String) =
|
||||
MethodInfo.GetInfo<TestParams, TestResponse>(
|
||||
summary = "Another get test",
|
||||
description = "testing more",
|
||||
responseInfo = testGetResponse,
|
||||
securitySchemes = security.toSet()
|
||||
describe("OAuth Authentication") {
|
||||
it("Can create an Oauth schema with all possible flows") {
|
||||
// arrange
|
||||
val flows = OAuth.Flows(
|
||||
implicit = OAuth.Flows.Implicit(
|
||||
"https://accounts.google.com/o/oauth2/auth",
|
||||
scopes = mapOf("test" to "is a cool scope", "this" to "is also cool")
|
||||
),
|
||||
authorizationCode = OAuth.Flows.AuthorizationCode("https://accounts.google.com/o/oauth2/auth"),
|
||||
password = OAuth.Flows.Password("https://accounts.google.com/o/oauth2/auth"),
|
||||
clientCredentials = OAuth.Flows.ClientCredential("https://accounts.google.com/token")
|
||||
)
|
||||
|
||||
val authConfig = object : OAuthConfiguration {
|
||||
override val flows: OAuth.Flows = flows
|
||||
override val name: String = AuthConfigName.OAuth
|
||||
}
|
||||
|
||||
// act
|
||||
openApiTest("notarized_oauth_all_flows.json") {
|
||||
setupOauth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,18 +0,0 @@
|
||||
package io.bkbn.kompendium.auth.util
|
||||
|
||||
import java.io.File
|
||||
|
||||
object TestData {
|
||||
object AuthConfigName {
|
||||
const val Basic = "basic"
|
||||
const val JWT = "jwt"
|
||||
}
|
||||
|
||||
const val getRoutePath = "/test"
|
||||
|
||||
fun getFileSnapshot(fileName: String): String {
|
||||
val snapshotPath = "src/test/resources"
|
||||
val file = File("$snapshotPath/$fileName")
|
||||
return file.readText()
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package io.bkbn.kompendium.auth.util
|
||||
|
||||
import io.bkbn.kompendium.annotations.KompendiumField
|
||||
import io.bkbn.kompendium.annotations.KompendiumParam
|
||||
import io.bkbn.kompendium.annotations.ParamType
|
||||
|
||||
data class TestParams(
|
||||
@KompendiumParam(ParamType.PATH) val a: String,
|
||||
@KompendiumParam(ParamType.QUERY) val aa: Int
|
||||
)
|
||||
|
||||
data class TestRequest(
|
||||
@KompendiumField(name = "field_name")
|
||||
val b: Double,
|
||||
val aaa: List<Long>
|
||||
)
|
||||
|
||||
data class TestResponse(val c: String)
|
||||
|
@ -0,0 +1,92 @@
|
||||
package io.bkbn.kompendium.auth.util
|
||||
|
||||
import io.bkbn.kompendium.auth.Notarized.notarizedAuthenticate
|
||||
import io.bkbn.kompendium.auth.configuration.SecurityConfiguration
|
||||
import io.bkbn.kompendium.core.Notarized.notarizedGet
|
||||
import io.bkbn.kompendium.core.fixtures.TestParams
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.OAuthServerSettings
|
||||
import io.ktor.auth.UserIdPrincipal
|
||||
import io.ktor.auth.basic
|
||||
import io.ktor.auth.jwt.jwt
|
||||
import io.ktor.auth.oauth
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.routing.routing
|
||||
|
||||
fun Application.setupOauth() {
|
||||
install(Authentication) {
|
||||
oauth("oauth") {
|
||||
urlProvider = { "http://localhost:8080/callback" }
|
||||
client = HttpClient(CIO)
|
||||
providerLookup = {
|
||||
OAuthServerSettings.OAuth2ServerSettings(
|
||||
name = "google",
|
||||
authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
|
||||
requestMethod = HttpMethod.Post,
|
||||
clientId = System.getenv("GOOGLE_CLIENT_ID"),
|
||||
clientSecret = System.getenv("GOOGLE_CLIENT_SECRET"),
|
||||
defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.configBasicAuth() {
|
||||
install(Authentication) {
|
||||
basic(AuthConfigName.Basic) {
|
||||
realm = "Ktor Server"
|
||||
validate { credentials ->
|
||||
if (credentials.name == credentials.password) {
|
||||
UserIdPrincipal(credentials.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.notarizedAuthRoute(authConfig: SecurityConfiguration) {
|
||||
routing {
|
||||
notarizedAuthenticate(authConfig) {
|
||||
route("/test") { notarizedGet(testGetInfo(authConfig.name)) {
|
||||
call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.configJwtAuth() {
|
||||
install(Authentication) {
|
||||
jwt(AuthConfigName.JWT) {
|
||||
realm = "Ktor server"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun testGetInfo(vararg security: String) =
|
||||
GetInfo<TestParams, TestResponse>(
|
||||
summary = "Another get test",
|
||||
description = "testing more",
|
||||
responseInfo = TestResponseInfo.testGetResponse,
|
||||
securitySchemes = security.toSet()
|
||||
)
|
||||
|
||||
object AuthConfigName {
|
||||
const val Basic = "basic"
|
||||
const val JWT = "jwt"
|
||||
const val OAuth = "oauth"
|
||||
}
|
@ -1,75 +1,94 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : { },
|
||||
"servers" : [ ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false,
|
||||
"security" : [ {
|
||||
"basic" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
"deprecated": false,
|
||||
"security": [
|
||||
{
|
||||
"basic": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : {
|
||||
"basic" : {
|
||||
"type" : "http",
|
||||
"scheme" : "basic"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"basic": {
|
||||
"type": "http",
|
||||
"scheme": "basic"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,75 +1,95 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : { },
|
||||
"servers" : [ ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false,
|
||||
"security" : [ {
|
||||
"jwt" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
"deprecated": false,
|
||||
"security": [
|
||||
{
|
||||
"jwt": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : {
|
||||
"jwt" : {
|
||||
"type" : "http",
|
||||
"scheme" : "bearer"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"jwt": {
|
||||
"bearerFormat": "JWT",
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : { },
|
||||
"servers" : [ ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false,
|
||||
"security" : [ {
|
||||
"jwt" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : {
|
||||
"jwt" : {
|
||||
"type" : "apiKey",
|
||||
"name" : "x-api-key",
|
||||
"in" : "header"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : { },
|
||||
"servers" : [ ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false,
|
||||
"security" : [ {
|
||||
"jwt" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : {
|
||||
"jwt" : {
|
||||
"type" : "http",
|
||||
"scheme" : "oauth"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : { },
|
||||
"servers" : [ ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false,
|
||||
"security" : [ {
|
||||
"jwt1" : [ ],
|
||||
"jwt2" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : {
|
||||
"jwt1" : {
|
||||
"type" : "apiKey",
|
||||
"name" : "x-api-key-1",
|
||||
"in" : "header"
|
||||
},
|
||||
"jwt2" : {
|
||||
"type" : "apiKey",
|
||||
"name" : "x-api-key-2",
|
||||
"in" : "header"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false,
|
||||
"security": [
|
||||
{
|
||||
"oauth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"oauth": {
|
||||
"flows": {
|
||||
"implicit": {
|
||||
"authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
|
||||
"scopes": {
|
||||
"test": "is a cool scope",
|
||||
"this": "is also cool"
|
||||
}
|
||||
},
|
||||
"authorizationCode": {
|
||||
"authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
|
||||
"scopes": {}
|
||||
},
|
||||
"password": {
|
||||
"tokenUrl": "https://accounts.google.com/o/oauth2/auth",
|
||||
"scopes": {}
|
||||
},
|
||||
"clientCredentials": {
|
||||
"tokenUrl": "https://accounts.google.com/token",
|
||||
"scopes": {}
|
||||
}
|
||||
},
|
||||
"type": "oauth2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
Reference in New Issue
Block a user