@@ -3,7 +3,6 @@ package org.sourcegrade.lab.hub.http
33import io.ktor.client.HttpClient
44import io.ktor.client.call.body
55import io.ktor.client.engine.cio.CIO
6- import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
76import io.ktor.client.request.get
87import io.ktor.client.request.header
98import io.ktor.http.HttpMethod
@@ -26,6 +25,7 @@ import io.ktor.server.response.respond
2625import io.ktor.server.response.respondRedirect
2726import io.ktor.server.response.respondText
2827import io.ktor.server.routing.get
28+ import io.ktor.server.routing.route
2929import io.ktor.server.routing.routing
3030import io.ktor.server.sessions.Sessions
3131import io.ktor.server.sessions.clear
@@ -43,14 +43,17 @@ import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransacti
4343import org.sourcegrade.lab.hub.models.User
4444import org.sourcegrade.lab.hub.models.Users
4545import java.io.File
46+ import kotlin.collections.set
4647import kotlin.time.Duration.Companion.hours
48+ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation as ClientContentNegotiation
49+ import io.ktor.server.plugins.contentnegotiation.ContentNegotiation as ServerContentNegotiation
4750
4851fun Application.authenticationModule () {
4952 val ktorEnv = environment
5053
5154 val httpClient =
5255 HttpClient (CIO ) {
53- install(ContentNegotiation ) {
56+ install(ClientContentNegotiation ) {
5457 json(
5558 Json {
5659 encodeDefaults = false
@@ -85,81 +88,96 @@ fun Application.authenticationModule() {
8588
8689 client = httpClient
8790
88- val oauthSettings = OAuthServerSettings .OAuth2ServerSettings (
89- name = " Authentik" ,
90- authorizeUrl = ktorEnv.config.property(" ktor.oauth.authorizeUrl" ).getString(),
91- accessTokenUrl = ktorEnv.config.property(" ktor.oauth.accessTokenUrl" ).getString(),
92- requestMethod = HttpMethod .Post ,
93- clientId = ktorEnv.config.property(" ktor.oauth.clientId" ).getString(),
94- clientSecret = ktorEnv.config.property(" ktor.oauth.clientSecret" ).getString(),
95- defaultScopes = ktorEnv.config.tryGetString(" ktor.oauth.scopes" )
96- ?.split(" " )
97- ? : listOf (" openid" , " profile" , " email" ),
98- onStateCreated = { call, state ->
99- // saves new state with redirect url value
100- call.request.queryParameters[" redirectUrl" ]?.let {
101- redirects[state] = it
102- }
103- },
104- )
105-
91+ val oauthSettings =
92+ OAuthServerSettings .OAuth2ServerSettings (
93+ name = " Authentik" ,
94+ authorizeUrl = ktorEnv.config.property(" ktor.oauth.authorizeUrl" )
95+ .getString(),
96+ accessTokenUrl = ktorEnv.config.property(" ktor.oauth.accessTokenUrl" )
97+ .getString(),
98+ requestMethod = HttpMethod .Post ,
99+ clientId = ktorEnv.config.property(" ktor.oauth.clientId" )
100+ .getString(),
101+ clientSecret = ktorEnv.config.property(" ktor.oauth.clientSecret" )
102+ .getString(),
103+ defaultScopes = ktorEnv.config.tryGetString(" ktor.oauth.scopes" )
104+ ?.split(" " )
105+ ? : listOf (" openid" , " profile" , " email" ),
106+ onStateCreated = { call, state ->
107+ // saves new state with redirect url value
108+ call.request.queryParameters[" redirectUrl" ]?.let {
109+ redirects[state] = it
110+ }
111+ },
112+ )
106113 providerLookup = { oauthSettings }
107114 }
108115 }
109116 routing {
110- authenticate(" Authentik" ) {
111- get(" /api/session/login" ) {
112- // Redirects to 'authorizeUrl' automatically
117+ route(" /api/session" ) {
118+ install(ServerContentNegotiation ) {
119+ json(
120+ Json {
121+ prettyPrint = true
122+ isLenient = true
123+ ignoreUnknownKeys = true
124+ },
125+ )
113126 }
127+ authenticate(" Authentik" ) {
128+ get(" login" ) {
129+ // Redirects to 'authorizeUrl' automatically
130+ }
114131
115- get(callback) {
116- val principal: OAuthAccessTokenResponse .OAuth2 = checkNotNull(call.principal()) { " No principal" }
117-
118- val userInfo =
119- httpClient.get(
120- this @authenticationModule.environment.config.tryGetString(" ktor.oauth.userInfoUrl" )
121- ? : throw IllegalStateException (" Missing OAuth user info Url" ),
122- ) {
123- header(" Authorization" , " Bearer ${principal.accessToken} " )
124- }.body<OAuthUserInfo >()
125-
126- // find user in db
127-
128- val user =
129- newSuspendedTransaction {
130- User .find { Users .email eq userInfo.email }.firstOrNull()
131- } ? : newSuspendedTransaction {
132- User .new {
133- username = userInfo.preferredUsername
134- email = userInfo.email
132+ get(" callback" ) {
133+ val principal: OAuthAccessTokenResponse .OAuth2 = checkNotNull(call.principal()) { " No principal" }
134+
135+ val userInfo =
136+ httpClient.get(
137+ this @authenticationModule.environment.config.tryGetString(" ktor.oauth.userInfoUrl" )
138+ ? : throw IllegalStateException (" Missing OAuth user info Url" ),
139+ ) {
140+ header(" Authorization" , " Bearer ${principal.accessToken} " )
141+ }.body<OAuthUserInfo >()
142+
143+ // find user in db
144+
145+ val user =
146+ newSuspendedTransaction {
147+ User .find { Users .email eq userInfo.email }.firstOrNull()
148+ } ? : newSuspendedTransaction {
149+ User .new {
150+ username = userInfo.preferredUsername
151+ email = userInfo.email
152+ }
135153 }
136- }
137154
138- val session =
139- UserSession (
140- user.id.value,
141- checkNotNull(principal.state) { " No state" },
142- principal.accessToken,
143- userInfo.email,
144- )
145-
146- call.sessions.set(session)
147- principal.state?.let { state ->
148- redirects[state]?.let { redirect ->
149- call.respondRedirect(redirect)
150- return @get
155+ val session =
156+ UserSession (
157+ user.id.value,
158+ checkNotNull(principal.state) { " No state" },
159+ principal.accessToken,
160+ userInfo.email,
161+ )
162+
163+ call.sessions.set(session)
164+ principal.state?.let { state ->
165+ redirects[state]?.let { redirect ->
166+ call.respondRedirect(redirect)
167+ return @get
168+ }
151169 }
170+ call.respondRedirect(" /" )
152171 }
153- call.respondRedirect(" /" )
154- }
155172
156- get(" /api/session/logout" ) {
157- call.sessions.clear<UserSession >()
158- call.respondRedirect(" /" )
173+ get(" logout" ) {
174+ call.sessions.clear<UserSession >()
175+ call.respondRedirect(" /" )
176+ }
177+ }
178+ get(" current-user" ) {
179+ withUser { call.respond(it.toDTO()) }
159180 }
160- }
161- get(" /api/session/current-user" ) {
162- withUser { call.respond(it.toDTO()) }
163181 }
164182 }
165183}
0 commit comments