scala-pocketbase-oauth-attempt/src/main/scala/example/AuthService.scala

155 lines
4.5 KiB
Scala

package example
import AuthService._
import example.pocketbase.Api
import upickle.default._
import example.pocketbase.Models._
case class AuthService()(implicit cc: castor.Context, log: cask.Logger)
extends cask.Routes {
@cask.get("/")
def getIndex(request: cask.Request) = {
println("hellololo")
val authCookieOpt = request.cookies.get(authCookieName)
authCookieOpt match {
case None =>
cask.Redirect("/login")
case Some(authCookie) =>
val jwt = authCookie.value
???
}
}
@cask.get("/login")
def getLoginPage() = {
// render auth page with the available oauth providers
val authOptions = pocketbaseApi.listAuthMethods()
val options = s"got following auth opitons: $authOptions"
// save states and verifiers into cookie
val oauthVerificationCookie = authOptions.toOauthCookieInfo()
val yoyo = write(oauthVerificationCookie)
val githubOption = authOptions.authProviders.find(_.name == "github")
val githubRedirect =
githubOption.map(_.authUrl).getOrElse("") ++ getRedirectUrl("github")
val html = s"""
<h1>good enough, right</h1>
<p>$options</p>
<p>will use ${githubRedirect}</p>
<a href="${githubRedirect}">Go to github</a>
"""
cask.Response(
html,
headers = Seq("Content-Type" -> "text/html;charset=UTF-8"),
cookies = Seq(cask.Cookie(name = oauthVerifiersCookieName, value = yoyo))
)
}
/** @oauthVerifiers
* \- required cookie from start of oauth initialization cask will fail
* request if this is not present
*/
@cask.get(s"${baseRedirectUrl}/:provider")
def receiveOauthRedirect(
provider: String,
state: String,
code: String,
request: cask.Request
) = {
println(
s"received redirect for $provider with state: $state and code: $code"
)
val authVerifierOpt = request.cookies
.get(oauthVerifiersCookieName)
.map(cookie => read[OauthInfoCookie](cookie.value))
.flatMap(_.providersVerification.find(_.name == provider))
val resultOpt = for {
authVerifier <- authVerifierOpt
_ <- Some(()).filter(_ => state == authVerifier.state)
pocketbaseAuthResult <- pocketbaseApi
.authWithOauth(
provider = provider,
code = code,
verifier = authVerifier.codeVerifier,
redirectUrl = getRedirectUrl(provider)
)
.toOption
} yield pocketbaseAuthResult
val okMessageFirst = resultOpt match {
case None =>
// i guess with the SSR i'll need to return message about unsuccessful auth?
s"""
<h1>Auth unsuccessful</h1>
"""
case Some(result) =>
// this is already fully successful auth
s"""
<h1>Ok, good</h1>
<p>user should be already created, current jwt : ${result.token}</p>
<p>the account is on ${result.record.email} and ${result.record.username}</p>
"""
}
/*
*
get provider from path param, get verifiers and state from cookie,
if cookie not present - abort,
if state doesn't fit one from redirect params - abort
issue 'auth with oauth 2' and based on response code - set the cookie with jwt
and delete the state\verifiers cookie
but then what? i guess call for redirect to root page again?
which should trigger auth check and main page render?
*/
import java.time.Instant
cask.Response(
okMessageFirst,
headers = Seq("Content-Type" -> "text/html;charset=UTF-8"),
cookies = Seq(
cask.Cookie(
name = oauthVerifiersCookieName,
value = "",
expires = Instant.now()
)
)
)
}
initialize()
}
object AuthService {
val authCookieName = "auth"
// this is to share info between /auth page and /redirect-landing
// can be improved by generating same state and codeVerifier for all oauth links
// and can also be stored purely on the backend - a tad more secure, i guess
// but if cookie is under https, should be ok
val oauthVerifiersCookieName = "oauthVerifiers"
val pocketbaseApi = Api("http://127.0.0.1:8090")
val selfUri = "http://127.0.0.1:8080"
// i guess TOOD put that into config
val baseRedirectUrl = "/api/oauth2-redirect"
/*
* let's have separate redirect urls for different providers
* if we'd have same url for all providers, we'll need way to figure out from state which one was called on front end.
* it's possible, but path param looks simpler right now
* github redirect : http://127.0.0.1:8080/api/oauth2-redirect/github
*/
def getRedirectUrl(provider: String): String =
s"${selfUri}${baseRedirectUrl}/${provider}"
}