initial hardcoded github oauth button
This commit is contained in:
parent
8c255ed812
commit
c87abb6956
|
@ -1,3 +1,26 @@
|
|||
#+title: Auth Notes
|
||||
* starting the pocketbase as framework
|
||||
https://pocketbase.io/docs/go-overview/
|
||||
* plan
|
||||
** DONE start pocketbase
|
||||
** DONE add middlewares for cookie session
|
||||
** TODO add index page, that will have either "current user" or 'login' link
|
||||
** TODO 'login' link should open dialog with oauth providers
|
||||
so, i want a window with available oauth providers,
|
||||
to trigger the js code from example
|
||||
https://pocketbase.io/docs/authentication/
|
||||
( all in one, recommended )
|
||||
|
||||
let's get configured providers in the go code,
|
||||
add as slice of strings, and in template create buttons for each of those
|
||||
with js code from the example
|
||||
** TODO i guess i would also like to send htmx event for reloading the page
|
||||
on successful auth?
|
||||
** TODO add one more page that checks auth
|
||||
** TODO add tailwind styling
|
||||
** TODO package static into single binary
|
||||
** TODO write nix build
|
||||
** TODO write nixos module
|
||||
** TODO add docker image from nix
|
||||
*** TODO add cli for port and host
|
||||
** TODO add readme and comments
|
||||
|
|
8
go.mod
8
go.mod
|
@ -2,7 +2,11 @@ module sunshine.industries/auth-pocketbase-attempt
|
|||
|
||||
go 1.20
|
||||
|
||||
require github.com/pocketbase/pocketbase v0.18.9
|
||||
require (
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/pocketbase/pocketbase v0.18.9
|
||||
github.com/spf13/cast v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
|
@ -43,14 +47,12 @@ require (
|
|||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/pocketbase/dbx v1.10.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
|
|
7
main.go
7
main.go
|
@ -2,15 +2,16 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
// "os"
|
||||
|
||||
"github.com/pocketbase/pocketbase"
|
||||
// "github.com/pocketbase/pocketbase/apis"
|
||||
// "github.com/pocketbase/pocketbase/core"
|
||||
"sunshine.industries/auth-pocketbase-attempt/middleware"
|
||||
"sunshine.industries/auth-pocketbase-attempt/pages"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := pocketbase.New()
|
||||
middleware.AddCookieSessionMiddleware(app)
|
||||
pages.AddPageRoutes(app)
|
||||
|
||||
if err := app.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tokens"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
const AuthCookieName = "Auth"
|
||||
|
||||
func AddCookieSessionMiddleware(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.Use(loadAuthContextFromCookie(app))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// fires for every auth collection
|
||||
app.OnRecordAuthRequest().Add(func(e *core.RecordAuthEvent) error {
|
||||
log.Println(e.HttpContext)
|
||||
log.Println(e.Record)
|
||||
log.Println(e.Token)
|
||||
log.Println(e.Meta)
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: e.Token,
|
||||
Path: "/",
|
||||
})
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: "username",
|
||||
Value: e.Record.Username(),
|
||||
})
|
||||
return nil
|
||||
})
|
||||
app.OnAdminAuthRequest().Add(func(e *core.AdminAuthEvent) error {
|
||||
log.Println(e.HttpContext)
|
||||
log.Println(e.Admin)
|
||||
log.Println(e.Token)
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: e.Token,
|
||||
Path: "/",
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func loadAuthContextFromCookie(app core.App) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
tokenCookie, err := c.Request().Cookie(AuthCookieName)
|
||||
if err != nil || tokenCookie.Value == "" {
|
||||
return next(c) // no token cookie
|
||||
}
|
||||
|
||||
token := tokenCookie.Value
|
||||
|
||||
claims, _ := security.ParseUnverifiedJWT(token)
|
||||
tokenType := cast.ToString(claims["type"])
|
||||
|
||||
switch tokenType {
|
||||
case tokens.TypeAdmin:
|
||||
admin, err := app.Dao().FindAdminByToken(
|
||||
token,
|
||||
app.Settings().AdminAuthToken.Secret,
|
||||
)
|
||||
if err == nil && admin != nil {
|
||||
// "authenticate" the admin
|
||||
c.Set(apis.ContextAdminKey, admin)
|
||||
someData := struct {
|
||||
username string
|
||||
email string
|
||||
} {
|
||||
admin.Email,
|
||||
admin.Created.String(),
|
||||
}
|
||||
fmt.Printf("triggering the middlewar for cookie %v and err %v\n", someData, err)
|
||||
}
|
||||
|
||||
case tokens.TypeAuthRecord:
|
||||
record, err := app.Dao().FindAuthRecordByToken(
|
||||
token,
|
||||
app.Settings().RecordAuthToken.Secret,
|
||||
)
|
||||
if err == nil && record != nil {
|
||||
// "authenticate" the app user
|
||||
c.Set(apis.ContextAuthRecordKey, record)
|
||||
someData := struct {
|
||||
username string
|
||||
email string
|
||||
} {
|
||||
record.Username(),
|
||||
record.Email(),
|
||||
}
|
||||
fmt.Printf("triggering the middlewar for cookie %v and err %v\n", someData, err)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package pages
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
//go:embed templates
|
||||
var templatesFS embed.FS
|
||||
|
||||
func AddPageRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(getIndexPageRoute(app))
|
||||
app.OnBeforeServe().Add(getAuthPageRoute(app))
|
||||
}
|
||||
|
||||
// render and return index page
|
||||
func getIndexPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func (e *core.ServeEvent) error {
|
||||
e.Router.GET("/", func(c echo.Context) error {
|
||||
// first collect data
|
||||
info := apis.RequestInfo(c)
|
||||
admin := info.Admin // nil if not authenticated as admin
|
||||
record := info.AuthRecord // nil if not authenticated as regular auth record
|
||||
|
||||
isGuest := admin == nil && record == nil
|
||||
coolMessage := fmt.Sprintf("got admin %v and record %v. is guest: %t", admin, record, isGuest)
|
||||
fmt.Print(coolMessage)
|
||||
|
||||
username := ""
|
||||
switch {
|
||||
case admin != nil:
|
||||
username = admin.Email
|
||||
case record != nil:
|
||||
username = record.Username()
|
||||
}
|
||||
|
||||
oauthProviders := app.Settings().NamedAuthProviderConfigs()
|
||||
oauthProviderNames := make([]string, 0, len(oauthProviders))
|
||||
for name, config := range oauthProviders {
|
||||
if config.Enabled {
|
||||
oauthProviderNames = append(oauthProviderNames, name)
|
||||
}
|
||||
}
|
||||
fmt.Printf(">> enabled providers names %+v\n", oauthProviderNames)
|
||||
|
||||
indexPageData := struct {
|
||||
IsGuest, IsAdmin bool
|
||||
Username string
|
||||
EnabledOauthProviders []string
|
||||
}{
|
||||
IsGuest: isGuest,
|
||||
IsAdmin: admin != nil,
|
||||
Username: username,
|
||||
EnabledOauthProviders: oauthProviderNames,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
templateName := "templates/index.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, indexPageData); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, instantiatedTemplate.String())
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// render and return login page with configured oauth providers
|
||||
func getAuthPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func (e *core.ServeEvent) error {
|
||||
e.Router.GET("/login", func(c echo.Context) error {
|
||||
|
||||
templateName := "templates/login.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, nil); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, instantiatedTemplate.String())
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<!doctype html>
|
||||
<html class="no-js" lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>index page</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<!-- Place favicon.ico in the root directory -->
|
||||
<script
|
||||
defer
|
||||
src="https://cdn.jsdelivr.net/gh/pocketbase/js-sdk@master/dist/pocketbase.umd.js"
|
||||
></script>
|
||||
<script defer type="text/javascript">
|
||||
async function callOauth(providerName) {
|
||||
const pb = new PocketBase("http://127.0.0.1:8090");
|
||||
|
||||
// This method initializes a one-off realtime subscription and will
|
||||
// open a popup window with the OAuth2 vendor page to authenticate.
|
||||
//
|
||||
// Once the external OAuth2 sign-in/sign-up flow is completed, the popup
|
||||
// window will be automatically closed and the OAuth2 data sent back
|
||||
// to the user through the previously established realtime connection.
|
||||
const authData = await pb
|
||||
.collection("users")
|
||||
.authWithOAuth2({ provider: providerName });
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to index page</h1>
|
||||
<!--[if lt IE 8]>
|
||||
<p class="browserupgrade">
|
||||
You are using an <strong>outdated</strong> browser. Please
|
||||
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
|
||||
your experience.
|
||||
</p<dialog open>
|
||||
<p>Greetings, one and all!</p>
|
||||
<form method="dialog">
|
||||
<button>OK</button>
|
||||
</form>
|
||||
</dialog>
|
||||
>
|
||||
<![endif]-->
|
||||
<p>{{ .Username }}</p>
|
||||
<dialog open>
|
||||
<p>Greetings, one and all!</p>
|
||||
<button onClick="callOauth('github')">OK!</button>
|
||||
</dialog>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue