155 lines
4.5 KiB
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}"
|
|
}
|