mirror of
https://codeberg.org/readeck/readeck.git
synced 2026-05-19 11:00:36 +00:00
Detach config and auth/signin from oidc
- moved the shared auth layout to components (used by signin, oauth and oidc) - moved the post login redirects to internal/server - load OIDC configuration during app initialization - added provisioning tests
This commit is contained in:
@@ -37,3 +37,13 @@ func NewLayout() (l *Layout) {
|
||||
Head: templ.NopComponent,
|
||||
}
|
||||
}
|
||||
|
||||
// AuthLayout is the layout used on all the authentication pages.
|
||||
func AuthLayout(title string) templ.Component {
|
||||
return defaultLayout.AuthBase(title)
|
||||
}
|
||||
|
||||
// AuthError is the component for the authentication error pages.
|
||||
func AuthError(status int) templ.Component {
|
||||
return defaultLayout.AuthError(status)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package components
|
||||
|
||||
import "net/http"
|
||||
|
||||
// BasePage renders the layout's base page with head elements.
|
||||
templ (c *Layout) BasePage(title string) {
|
||||
<!DOCTYPE html>
|
||||
@@ -206,3 +208,36 @@ templ (c *Layout) SideMenuStdWrapper() {
|
||||
{ children... }
|
||||
</main>
|
||||
}
|
||||
|
||||
templ (c *Layout) authHeader() {
|
||||
<h1 class="max-w-sm mt-12 mx-auto px-8">
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 567 128" class="w-full fill-gray-900">
|
||||
<use href={ Asset(ctx, "img/logo-text.svg") + "#main" }></use>
|
||||
</svg>
|
||||
</div>
|
||||
</h1>
|
||||
}
|
||||
|
||||
templ (c *Layout) AuthBase(title string) {
|
||||
@BasePage(title) {
|
||||
@c.authHeader()
|
||||
<main class="w-full max-w-sm p-8 mt-6 mx-auto bg-gray-100 text-gray-dark rounded-md shadow-md" id="content">
|
||||
{ children... }
|
||||
</main>
|
||||
@CustomTemplate("auth/footer.html.tmpl", nil)
|
||||
}
|
||||
}
|
||||
|
||||
templ (c *Layout) AuthError(status int) {
|
||||
@BasePage(L(ctx).Gettext("Error")) {
|
||||
@c.authHeader()
|
||||
<main class="w-full max-w-sm p-8 mt-6 mx-auto text-center border border-red-800 bg-red-100 rounded-md" id="content">
|
||||
<h2 class="title text-h3">{ L(ctx).Gettext("An error occurred") }</h2>
|
||||
<p>{ http.StatusText(status) }</p>
|
||||
<p class="mt-8">
|
||||
<a class="link font-semibold" href={ URL(ctx, "/") }>{ L(ctx).Gettext("Go back to Readeck") }</a>
|
||||
</p>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+236
-26
@@ -14,6 +14,8 @@ package components
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "net/http"
|
||||
|
||||
// BasePage renders the layout's base page with head elements.
|
||||
func (c *Layout) BasePage(title string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
@@ -43,7 +45,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(L(ctx).Tag.String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 11, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 13, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -56,7 +58,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 16, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 18, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -69,7 +71,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(CSPNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 19, Col: 49}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 21, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -82,7 +84,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var5 templ.SafeURL
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(Asset(ctx, "bundle.css"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 21, Col: 57}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 23, Col: 57}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -95,7 +97,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(CSPNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 21, Col: 81}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 23, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -108,7 +110,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(Asset(ctx, "bundle.js"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 22, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 24, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -121,7 +123,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(CSPNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 22, Col: 78}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 24, Col: 78}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -134,7 +136,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(CSPNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 23, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 25, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -147,7 +149,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var10 templ.SafeURL
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(Asset(ctx, "img/fi/favicon.ico"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 30, Col: 59}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 32, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -160,7 +162,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var11 templ.SafeURL
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(Asset(ctx, `img/fi/favicon.svg`))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 31, Col: 59}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 33, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -173,7 +175,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var12 templ.SafeURL
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(Asset(ctx, `img/fi/apple-touch-icon.png`))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 32, Col: 80}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 34, Col: 80}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -186,7 +188,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var13 templ.SafeURL
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx, "/manifest.webmanifest"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 33, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 35, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -211,7 +213,7 @@ func (c *Layout) BasePage(title string) templ.Component {
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(CSPNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 50, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 52, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -297,7 +299,7 @@ func (c *Layout) Menu(menuBtn templ.Component) templ.Component {
|
||||
var templ_7745c5c3_Var17 templ.SafeURL
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx, "/"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 70, Col: 27}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 72, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -310,7 +312,7 @@ func (c *Layout) Menu(menuBtn templ.Component) templ.Component {
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(L(ctx).Gettext("Home page"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 70, Col: 65}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 72, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -351,7 +353,7 @@ func (c *Layout) Menu(menuBtn templ.Component) templ.Component {
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(L(ctx).Gettext("Change color theme"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 88, Col: 51}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 90, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -372,7 +374,7 @@ func (c *Layout) Menu(menuBtn templ.Component) templ.Component {
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Change color theme"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 93, Col: 67}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 95, Col: 67}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -457,7 +459,7 @@ func (c *Layout) menuButton(name string) templ.Component {
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 118, Col: 14}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 120, Col: 14}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -478,7 +480,7 @@ func (c *Layout) menuButton(name string) templ.Component {
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 126, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 128, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -520,7 +522,7 @@ func (c *Layout) mainMenuItem(name, path, icon string, current bool) templ.Compo
|
||||
var templ_7745c5c3_Var25 templ.SafeURL
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx, path))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 132, Col: 26}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 134, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -533,7 +535,7 @@ func (c *Layout) mainMenuItem(name, path, icon string, current bool) templ.Compo
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 132, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 134, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -546,7 +548,7 @@ func (c *Layout) mainMenuItem(name, path, icon string, current bool) templ.Compo
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(current)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 132, Col: 66}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 134, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -567,7 +569,7 @@ func (c *Layout) mainMenuItem(name, path, icon string, current bool) templ.Compo
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 134, Col: 31}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 136, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -611,7 +613,7 @@ func QuickAccessMenu(items [][2]string) templ.Component {
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.ResolveAttributeValue(L(ctx).Gettext("Quick access menu"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 142, Col: 49}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 144, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var30)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -629,7 +631,7 @@ func QuickAccessMenu(items [][2]string) templ.Component {
|
||||
var templ_7745c5c3_Var31 templ.SafeURL
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs("#" + item[0])
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 144, Col: 31}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 146, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -642,7 +644,7 @@ func QuickAccessMenu(items [][2]string) templ.Component {
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(item[1])
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 144, Col: 56}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 146, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -906,4 +908,212 @@ func (c *Layout) SideMenuStdWrapper() templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Layout) authHeader() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var40 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var40 == nil {
|
||||
templ_7745c5c3_Var40 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<h1 class=\"max-w-sm mt-12 mx-auto px-8\"><div><svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 567 128\" class=\"w-full fill-gray-900\"><use href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var41 string
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.ResolveAttributeValue(Asset(ctx, "img/logo-text.svg") + "#main")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 216, Col: 57}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var41)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\"></use></svg></div></h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Layout) AuthBase(title string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var42 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var42 == nil {
|
||||
templ_7745c5c3_Var42 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var43 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = c.authHeader().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, " <main class=\"w-full max-w-sm p-8 mt-6 mx-auto bg-gray-100 text-gray-dark rounded-md shadow-md\" id=\"content\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var42.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</main>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = CustomTemplate("auth/footer.html.tmpl", nil).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = BasePage(title).Render(templ.WithChildren(ctx, templ_7745c5c3_Var43), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Layout) AuthError(status int) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var44 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var44 == nil {
|
||||
templ_7745c5c3_Var44 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var45 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = c.authHeader().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, " <main class=\"w-full max-w-sm p-8 mt-6 mx-auto text-center border border-red-800 bg-red-100 rounded-md\" id=\"content\"><h2 class=\"title text-h3\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("An error occurred"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 236, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</h2><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var47 string
|
||||
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(http.StatusText(status))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 237, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</p><p class=\"mt-8\"><a class=\"link font-semibold\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var48 templ.SafeURL
|
||||
templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx, "/"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 239, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var49 string
|
||||
templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Go back to Readeck"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/layout.templ`, Line: 239, Col: 95}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "</a></p></main>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = BasePage(L(ctx).Gettext("Error")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var45), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
"codeberg.org/readeck/readeck/internal/acls"
|
||||
"codeberg.org/readeck/readeck/internal/auth/oidc"
|
||||
"codeberg.org/readeck/readeck/internal/auth/users"
|
||||
"codeberg.org/readeck/readeck/internal/bookmarks"
|
||||
"codeberg.org/readeck/readeck/internal/db"
|
||||
@@ -159,6 +160,12 @@ func InitApp() {
|
||||
// Init ACLs
|
||||
acls.Load(configs.Config.Customize.ExtraPermissions...)
|
||||
|
||||
// Init OIDC
|
||||
oidc.InitCookieHandler(configs.Keys.OIDCKey())
|
||||
for k, v := range configs.Config.Auth.OIDC.Providers {
|
||||
oidc.Providers.Add(k, v.Name, v.URL, v.ClientID, v.ClientSecret, v.Groups, v.ProvisioningEnabled)
|
||||
}
|
||||
|
||||
// Init email sending
|
||||
email.InitSender()
|
||||
if !email.CanSendEmail() {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"codeberg.org/readeck/readeck/internal/auth/signin"
|
||||
"codeberg.org/readeck/readeck/internal/server"
|
||||
|
||||
. "codeberg.org/readeck/readeck/components"
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
type Views struct{}
|
||||
|
||||
templ (v Views) authCode(client *oauthClient, scopes []string) {
|
||||
@signin.SigninLayout(L(ctx).Gettext("Authorization required")) {
|
||||
@AuthLayout(L(ctx).Gettext("Authorization required")) {
|
||||
@v.form(client, scopes)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +23,7 @@ templ (v Views) deviceCode(
|
||||
step deviceCodeStep, f *deviceAuthorizationForm,
|
||||
client *oauthClient, req *deviceAuthorizationRequest, scopes []string,
|
||||
) {
|
||||
@signin.SigninLayout(L(ctx).Gettext("Authorization required")) {
|
||||
@AuthLayout(L(ctx).Gettext("Authorization required")) {
|
||||
switch step {
|
||||
case stepInvalid:
|
||||
<h2 class="font-semibold">{ L(ctx).Gettext("Error") }</h2>
|
||||
|
||||
Generated
+35
-36
@@ -15,7 +15,6 @@ import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"codeberg.org/readeck/readeck/internal/auth/signin"
|
||||
"codeberg.org/readeck/readeck/internal/server"
|
||||
|
||||
. "codeberg.org/readeck/readeck/components"
|
||||
@@ -63,7 +62,7 @@ func (v Views) authCode(client *oauthClient, scopes []string) templ.Component {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = signin.SigninLayout(L(ctx).Gettext("Authorization required")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = AuthLayout(L(ctx).Gettext("Authorization required")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -116,7 +115,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Error"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 30, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 29, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -129,7 +128,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("This code has expired or is not valid"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 31, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 30, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -151,7 +150,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Connect a device"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 34, Col: 77}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 33, Col: 77}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -164,7 +163,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var8 templ.SafeURL
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 35, Col: 36}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 34, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -190,7 +189,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Next"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 43, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 42, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -220,7 +219,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.UserCode.Value())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 48, Col: 69}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 47, Col: 69}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -244,7 +243,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Connect a device"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 51, Col: 77}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 50, Col: 77}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -257,7 +256,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Authorization request was denied."))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 53, Col: 58}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 52, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -270,7 +269,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 54, Col: 10}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 53, Col: 10}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -283,7 +282,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext(" You can now close this page."))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 55, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 54, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -305,7 +304,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Connect a device"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 59, Col: 77}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 58, Col: 77}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -323,7 +322,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Authorization granted. Connecting device..."))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 61, Col: 71}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 60, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -360,7 +359,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Device is connected!"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 75, Col: 46}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 74, Col: 46}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -373,7 +372,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 76, Col: 11}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 75, Col: 11}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -386,7 +385,7 @@ func (v Views) deviceCode(
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext(" You can now close this page."))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 77, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 76, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -404,7 +403,7 @@ func (v Views) deviceCode(
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = signin.SigninLayout(L(ctx).Gettext("Authorization required")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = AuthLayout(L(ctx).Gettext("Authorization required")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -440,7 +439,7 @@ func (v Views) backBtn() templ.Component {
|
||||
var templ_7745c5c3_Var22 templ.SafeURL
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx, "/"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 87, Col: 85}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 86, Col: 85}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -453,7 +452,7 @@ func (v Views) backBtn() templ.Component {
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Go back to Readeck"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 88, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 87, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -507,7 +506,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Authorization required"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 101, Col: 80}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 100, Col: 80}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -525,7 +524,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(client.Logo)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 104, Col: 51}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 103, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -554,7 +553,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Review permissions"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 113, Col: 70}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 112, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -576,7 +575,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 118, Col: 9}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 117, Col: 9}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -589,7 +588,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(x)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 119, Col: 7}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 118, Col: 7}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -607,7 +606,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Application information"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 123, Col: 75}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 122, Col: 75}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -620,7 +619,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Name"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 125, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 124, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -633,7 +632,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(client.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 125, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 124, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -646,7 +645,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Website"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 126, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 125, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -659,7 +658,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var34 templ.SafeURL
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(client.URI)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 126, Col: 70}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 125, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -672,7 +671,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(client.URI)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 126, Col: 101}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 125, Col: 101}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -685,7 +684,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Version"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 127, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 126, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -698,7 +697,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(client.SoftwareVersion)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 127, Col: 69}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 126, Col: 69}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -711,7 +710,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var38 templ.SafeURL
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinURLErrs(URL(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 129, Col: 43}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 128, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -724,7 +723,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var39 string
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Authorize"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 136, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 135, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -737,7 +736,7 @@ func (v Views) form(client *oauthClient, scopes []string) templ.Component {
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(L(ctx).Gettext("Deny"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 144, Col: 27}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/auth/oauth2/x-oauth.templ`, Line: 143, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"codeberg.org/readeck/readeck/internal/auth/signin"
|
||||
"codeberg.org/readeck/readeck/components"
|
||||
"codeberg.org/readeck/readeck/internal/server"
|
||||
"codeberg.org/readeck/readeck/internal/sessions"
|
||||
"codeberg.org/readeck/readeck/pkg/forms"
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
|
||||
// SetupRoutes mounts the routes for the auth domain.
|
||||
func SetupRoutes(srv *server.Server) {
|
||||
initCookieHandler()
|
||||
|
||||
srv.AddRoute("/login/oidc", newOIDCHandler())
|
||||
}
|
||||
|
||||
@@ -45,7 +43,7 @@ func newOIDCHandler() *oidcHandler {
|
||||
r.Use(
|
||||
server.Csrf,
|
||||
server.WithSession(),
|
||||
server.WithCustomErrorComponent(signin.Views{}.Error),
|
||||
server.WithCustomErrorComponent(components.AuthError),
|
||||
)
|
||||
|
||||
h := &oidcHandler{r}
|
||||
@@ -155,7 +153,7 @@ func (h *oidcHandler) oidcCallback(w http.ResponseWriter, r *http.Request) {
|
||||
tracker.Delete(w, r)
|
||||
|
||||
// Provisioning user
|
||||
form := info.provisioningForm()
|
||||
form := info.Form()
|
||||
slog.Debug("OIDC user",
|
||||
slog.Any("id", form.ID.Value()),
|
||||
slog.Any("username", form.Username.Value()),
|
||||
@@ -168,7 +166,7 @@ func (h *oidcHandler) oidcCallback(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := form.execute(provider)
|
||||
user, err := form.Execute()
|
||||
if err != nil {
|
||||
server.Err(w, r, err)
|
||||
return
|
||||
@@ -182,9 +180,9 @@ func (h *oidcHandler) oidcCallback(w http.ResponseWriter, r *http.Request) {
|
||||
sess.Save(w, r)
|
||||
|
||||
if sess.Payload.RequiresMFA {
|
||||
signin.RedirectToMFA(w, r, redir)
|
||||
server.RedirectToMFA(w, r, redir)
|
||||
return
|
||||
}
|
||||
|
||||
signin.Redirect(w, r, redir)
|
||||
server.RedirectNoLogin(w, r, redir)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
"codeberg.org/readeck/readeck/internal/auth/oidc"
|
||||
. "codeberg.org/readeck/readeck/internal/testing" //revive:disable:dot-imports
|
||||
)
|
||||
@@ -66,23 +65,12 @@ func (s *oidcServer) start(clientID string) func() {
|
||||
s.clientID = clientID
|
||||
s.nonce = ""
|
||||
|
||||
configs.Config.Auth.OIDC.Providers = map[string]configs.OIDCProvider{
|
||||
"test": {
|
||||
Name: "OIDC Test",
|
||||
URL: s.url,
|
||||
ClientID: s.clientID,
|
||||
ClientSecret: "client-secret",
|
||||
ProvisioningEnabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
oidc.Providers.Add("test", "OIDC Test", s.url, s.clientID, "client-secret", [][2]string{}, true)
|
||||
return s.stop
|
||||
}
|
||||
|
||||
func (s *oidcServer) stop() {
|
||||
s.ts.Close()
|
||||
|
||||
configs.Config.Auth.OIDC.Providers = map[string]configs.OIDCProvider{}
|
||||
oidc.Providers.Clear()
|
||||
}
|
||||
|
||||
|
||||
+84
-56
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
"codeberg.org/readeck/readeck/internal/server/urls"
|
||||
"codeberg.org/readeck/readeck/pkg/http/securecookie"
|
||||
)
|
||||
@@ -31,13 +30,13 @@ const (
|
||||
|
||||
var cookieHandler *securecookie.Handler
|
||||
|
||||
// initCookieHandler prepares the [securecookie.Handler]
|
||||
// InitCookieHandler prepares the [securecookie.Handler]
|
||||
// for the OIDC tracking cookie.
|
||||
func initCookieHandler() {
|
||||
func InitCookieHandler(key []byte) {
|
||||
cookieHandler = securecookie.NewHandler(
|
||||
securecookie.Key(configs.Keys.OIDCKey()),
|
||||
securecookie.Key(key),
|
||||
securecookie.WithPath(path.Join(urls.Prefix())),
|
||||
securecookie.WithMaxAge(configs.Config.Server.Session.MaxAge),
|
||||
securecookie.WithMaxAge(3600),
|
||||
securecookie.WithName(cookieName),
|
||||
)
|
||||
}
|
||||
@@ -86,7 +85,14 @@ func (c *cookie) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
// providerInfo is wrapper around [configs.OIDCProvider] that contains
|
||||
// a resolved [oidc.ProviderConfig] and a [oauth2.Config].
|
||||
type providerInfo struct {
|
||||
configs.OIDCProvider
|
||||
loaded bool
|
||||
|
||||
Name string
|
||||
URL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Groups [][2]string
|
||||
ProvisioningEnabled bool
|
||||
|
||||
Config providerConfig
|
||||
OAuth oauth2.Config
|
||||
@@ -112,47 +118,31 @@ var Providers = providerList{
|
||||
items: map[string]providerInfo{},
|
||||
}
|
||||
|
||||
func newProvider(ctx context.Context, cf *configs.OIDCProvider) (*providerInfo, error) {
|
||||
// XXX: handle configuration for providers without discovery route
|
||||
p, err := oidc.NewProvider(ctx, cf.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Items returns the provider list.
|
||||
func (pl *providerList) Items() map[string]providerInfo {
|
||||
return pl.items
|
||||
}
|
||||
|
||||
res := &providerInfo{OIDCProvider: *cf}
|
||||
if err = p.Claims(&res.Config); err != nil {
|
||||
return nil, err
|
||||
// Add adds a new, unloaded, provider to the list.
|
||||
func (pl *providerList) Add(id, name, url, clientID, clientSecret string, groups [][2]string, prov bool) *providerInfo {
|
||||
pl.Lock()
|
||||
defer pl.Unlock()
|
||||
p := providerInfo{
|
||||
Name: name,
|
||||
URL: url,
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Groups: groups,
|
||||
ProvisioningEnabled: prov,
|
||||
}
|
||||
|
||||
if len(res.Config.GrantTypesSupported) > 0 && !slices.Contains(res.Config.GrantTypesSupported, "authorization_code") {
|
||||
return nil, newError(
|
||||
errors.New("provider doesn't support authorization_code grant"),
|
||||
withStatus(http.StatusNotImplemented),
|
||||
withAttrs(slog.Any("grant_types", res.Config.GrantTypesSupported)),
|
||||
)
|
||||
}
|
||||
|
||||
if len(res.Config.ResponseTypesSupported) > 0 && !slices.Contains(res.Config.ResponseTypesSupported, "code") {
|
||||
return nil, newError(
|
||||
errors.New("provider doesn't support code response type"),
|
||||
withStatus(http.StatusNotImplemented),
|
||||
withAttrs(slog.Any("response_types", res.Config.ResponseTypesSupported)),
|
||||
)
|
||||
}
|
||||
|
||||
res.OAuth = oauth2.Config{
|
||||
ClientID: cf.ClientID,
|
||||
ClientSecret: cf.ClientSecret,
|
||||
RedirectURL: urls.AbsoluteURLContext(ctx, "/login/oidc").String(),
|
||||
Endpoint: p.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
|
||||
}
|
||||
|
||||
return res, nil
|
||||
pl.items[id] = p
|
||||
return &p
|
||||
}
|
||||
|
||||
// Clear removes all cached providers. Used by tests.
|
||||
func (pl *providerList) Clear() {
|
||||
pl.Lock()
|
||||
defer pl.Unlock()
|
||||
pl.items = map[string]providerInfo{}
|
||||
}
|
||||
|
||||
@@ -163,22 +153,60 @@ func (pl *providerList) get(ctx context.Context, id string) (*providerInfo, erro
|
||||
pl.Lock()
|
||||
defer pl.Unlock()
|
||||
|
||||
if cf, ok := pl.items[id]; ok {
|
||||
return &cf, nil
|
||||
p, ok := pl.items[id]
|
||||
if !ok {
|
||||
return nil, errNoProvider
|
||||
}
|
||||
|
||||
if cf, ok := configs.Config.Auth.OIDC.Providers[id]; ok {
|
||||
provider, err := newProvider(ctx, &cf)
|
||||
if err := p.load(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func (p *providerInfo) load(ctx context.Context) (err error) {
|
||||
var oidcProvider *oidc.Provider
|
||||
if !p.loaded {
|
||||
// XXX: handle configuration for providers without discovery route
|
||||
oidcProvider, err = oidc.NewProvider(ctx, p.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
provider.Groups = cf.Groups
|
||||
pl.items[id] = *provider
|
||||
return provider, nil
|
||||
|
||||
if err = oidcProvider.Claims(&p.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
p.loaded = true
|
||||
} else {
|
||||
oidcProvider = p.Config.NewProvider(ctx)
|
||||
}
|
||||
|
||||
return nil, errNoProvider
|
||||
if len(p.Config.GrantTypesSupported) > 0 && !slices.Contains(p.Config.GrantTypesSupported, "authorization_code") {
|
||||
return newError(
|
||||
errors.New("provider doesn't support authorization_code grant"),
|
||||
withStatus(http.StatusNotImplemented),
|
||||
withAttrs(slog.Any("grant_types", p.Config.GrantTypesSupported)),
|
||||
)
|
||||
}
|
||||
|
||||
if len(p.Config.ResponseTypesSupported) > 0 && !slices.Contains(p.Config.ResponseTypesSupported, "code") {
|
||||
return newError(
|
||||
errors.New("provider doesn't support code response type"),
|
||||
withStatus(http.StatusNotImplemented),
|
||||
withAttrs(slog.Any("response_types", p.Config.ResponseTypesSupported)),
|
||||
)
|
||||
}
|
||||
|
||||
p.OAuth = oauth2.Config{
|
||||
ClientID: p.ClientID,
|
||||
ClientSecret: p.ClientSecret,
|
||||
RedirectURL: urls.AbsoluteURLContext(ctx, "/login/oidc").String(),
|
||||
Endpoint: oidcProvider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthCodeURL returns the auth code URL needed to start the OAuth dance.
|
||||
@@ -189,7 +217,7 @@ func (p *providerInfo) AuthCodeURL(state, nonce, pkceVerifier string) string {
|
||||
// Exchange exchanges a code for an [oauth2.Token].
|
||||
// If the token contains an "id_token", it will check that it's
|
||||
// properly signed and have a matching nonce.
|
||||
func (p *providerInfo) Exchange(ctx context.Context, code, nonce, pkceVerifier string) (*oauth2.Token, *userInfo, error) {
|
||||
func (p *providerInfo) Exchange(ctx context.Context, code, nonce, pkceVerifier string) (*oauth2.Token, *UserInfo, error) {
|
||||
// Fetch token
|
||||
var options []oauth2.AuthCodeOption
|
||||
if pkceVerifier != "" {
|
||||
@@ -204,7 +232,7 @@ func (p *providerInfo) Exchange(ctx context.Context, code, nonce, pkceVerifier s
|
||||
// Verify id_token, if any
|
||||
rawID, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return token, &userInfo{provider: p}, nil
|
||||
return token, &UserInfo{Provider: p}, nil
|
||||
}
|
||||
|
||||
verifier := p.Config.NewProvider(ctx).Verifier(&oidc.Config{
|
||||
@@ -223,14 +251,14 @@ func (p *providerInfo) Exchange(ctx context.Context, code, nonce, pkceVerifier s
|
||||
}
|
||||
|
||||
// Retrieve the claims, it's ok to fail here.
|
||||
userInfo := &userInfo{provider: p}
|
||||
userInfo := &UserInfo{Provider: p}
|
||||
_ = tokenID.Claims(userInfo)
|
||||
|
||||
return token, userInfo, nil
|
||||
}
|
||||
|
||||
// GetUser returns a [userInfo] from the user info endpoint.
|
||||
func (p *providerInfo) GetUser(ctx context.Context, token *oauth2.Token) (*userInfo, error) {
|
||||
func (p *providerInfo) GetUser(ctx context.Context, token *oauth2.Token) (*UserInfo, error) {
|
||||
pr := p.Config.NewProvider(ctx)
|
||||
|
||||
info, err := pr.UserInfo(ctx, p.OAuth.TokenSource(ctx, token))
|
||||
@@ -238,8 +266,8 @@ func (p *providerInfo) GetUser(ctx context.Context, token *oauth2.Token) (*userI
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &userInfo{
|
||||
provider: p,
|
||||
res := &UserInfo{
|
||||
Provider: p,
|
||||
Email: info.Email,
|
||||
}
|
||||
if err = info.Claims(res); err != nil {
|
||||
|
||||
@@ -21,33 +21,35 @@ import (
|
||||
"codeberg.org/readeck/readeck/pkg/forms"
|
||||
)
|
||||
|
||||
type provisioningForm struct {
|
||||
// ProvisioningForm is the form used to provisioon a user.
|
||||
type ProvisioningForm struct {
|
||||
forms.Form
|
||||
userInfo *UserInfo
|
||||
|
||||
ID forms.TextField `json:"id" validate:"trim required"`
|
||||
Username forms.TextField `json:"username" validate:"trim is_valid_username"`
|
||||
Email forms.TextField `json:"email" validate:"trim is_valid_email"`
|
||||
Group forms.TextField `json:"group" validate:"required_or_nil group_choices"`
|
||||
}
|
||||
|
||||
// userInfo contains the information gathered after the token exchange.
|
||||
type userInfo struct {
|
||||
provider *providerInfo
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Username string `json:"preferred_username"`
|
||||
Email string `json:"email"`
|
||||
ReadeckUser string `json:"readeck_user"`
|
||||
ReadeckEmail string `json:"readeck_email"`
|
||||
ReadeckGroup string `json:"readeck_group"`
|
||||
Groups []string `json:"groups"`
|
||||
Group string `json:"-"`
|
||||
// UserInfo contains the information gathered after the token exchange.
|
||||
type UserInfo struct {
|
||||
Provider *providerInfo `json:"-"`
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Username string `json:"preferred_username"`
|
||||
Email string `json:"email"`
|
||||
Groups []string `json:"groups"`
|
||||
ReadeckUser string `json:"readeck_user"`
|
||||
ReadeckEmail string `json:"readeck_email"`
|
||||
ReadeckGroup string `json:"readeck_group"`
|
||||
}
|
||||
|
||||
func (ui *userInfo) id() uuid.UUID {
|
||||
func (ui *UserInfo) id() uuid.UUID {
|
||||
return uuid.NewSHA1(uuid.NameSpaceURL, []byte(ui.Issuer+"#"+ui.Subject))
|
||||
}
|
||||
|
||||
func (ui *userInfo) username() string {
|
||||
func (ui *UserInfo) username() string {
|
||||
switch {
|
||||
case ui.ReadeckUser != "":
|
||||
return ui.ReadeckUser
|
||||
@@ -58,7 +60,7 @@ func (ui *userInfo) username() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *userInfo) email() string {
|
||||
func (ui *UserInfo) email() string {
|
||||
switch {
|
||||
case ui.ReadeckEmail != "":
|
||||
return ui.ReadeckEmail
|
||||
@@ -67,12 +69,12 @@ func (ui *userInfo) email() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *userInfo) group() string {
|
||||
func (ui *UserInfo) group() string {
|
||||
if ui.ReadeckGroup != "" {
|
||||
return ui.ReadeckGroup
|
||||
}
|
||||
|
||||
for _, g := range ui.provider.Groups {
|
||||
for _, g := range ui.Provider.Groups {
|
||||
if slices.Contains(ui.Groups, g[0]) {
|
||||
return g[1]
|
||||
}
|
||||
@@ -80,7 +82,8 @@ func (ui *userInfo) group() string {
|
||||
return "user"
|
||||
}
|
||||
|
||||
func (ui *userInfo) provisioningForm() *provisioningForm {
|
||||
// Form returns a bound [ProvisioningForm] using the struct values.
|
||||
func (ui *UserInfo) Form() *ProvisioningForm {
|
||||
// Get some corner cases out the way first
|
||||
username := ui.username()
|
||||
email := ui.email()
|
||||
@@ -99,13 +102,14 @@ func (ui *userInfo) provisioningForm() *provisioningForm {
|
||||
"group": {ui.group()},
|
||||
}
|
||||
|
||||
f := forms.New[provisioningForm](context.Background())
|
||||
f := forms.New[ProvisioningForm](context.Background())
|
||||
f.userInfo = ui
|
||||
forms.BindValues(q, f)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *provisioningForm) errors() error {
|
||||
func (f *ProvisioningForm) errors() error {
|
||||
if len(f.Errors()) > 0 {
|
||||
return f.Errors()
|
||||
}
|
||||
@@ -119,7 +123,9 @@ func (f *provisioningForm) errors() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *provisioningForm) execute(provider *providerInfo) (*users.User, error) {
|
||||
// Execute performs the user provisioning.
|
||||
// It creates or updates a user depending on whether it exists already.
|
||||
func (f *ProvisioningForm) Execute() (*users.User, error) {
|
||||
tx, err := db.Q().Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -167,7 +173,7 @@ func (f *provisioningForm) execute(provider *providerInfo) (*users.User, error)
|
||||
now := time.Now().UTC()
|
||||
if user.IsAnonymous() {
|
||||
// Create user
|
||||
if !provider.ProvisioningEnabled {
|
||||
if !f.userInfo.Provider.ProvisioningEnabled {
|
||||
return errNoProvisioning
|
||||
}
|
||||
|
||||
@@ -184,7 +190,7 @@ func (f *provisioningForm) execute(provider *providerInfo) (*users.User, error)
|
||||
user.ExtID = new(f.ID.Value())
|
||||
user.ExtInfo = &users.ExtInfo{
|
||||
AddedOn: user.Created,
|
||||
Issuer: provider.URL,
|
||||
Issuer: f.userInfo.Issuer,
|
||||
}
|
||||
|
||||
ds := tx.Insert(db.TableUser).Rows(user).Prepared(true)
|
||||
@@ -207,7 +213,7 @@ func (f *provisioningForm) execute(provider *providerInfo) (*users.User, error)
|
||||
AddedOn: now,
|
||||
}
|
||||
}
|
||||
user.ExtInfo.Issuer = provider.URL
|
||||
user.ExtInfo.Issuer = f.userInfo.Issuer
|
||||
|
||||
_, err = tx.Update(db.TableUser).
|
||||
Set(user).Where(goqu.C("id").Eq(user.ID)).
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// SPDX-FileCopyrightText: © 2026 Olivier Meunier <olivier@neokraft.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
package oidc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"codeberg.org/readeck/readeck/internal/auth/oidc"
|
||||
"codeberg.org/readeck/readeck/internal/auth/users"
|
||||
. "codeberg.org/readeck/readeck/internal/testing" //revive:disable:dot-imports
|
||||
)
|
||||
|
||||
func TestProvisioning(t *testing.T) {
|
||||
app := NewTestApp(t)
|
||||
defer app.Close(t)
|
||||
|
||||
providerInfo := oidc.Providers.Add(
|
||||
"test", "OIDC Test", "https://example.org", "client-id", "client-secret",
|
||||
[][2]string{
|
||||
{"roles:admin", "admin"},
|
||||
{"roles:user", "user"},
|
||||
},
|
||||
true,
|
||||
)
|
||||
defer oidc.Providers.Clear()
|
||||
|
||||
user1, err := NewTestUser("bob", "bob@example.org", "abcd", "user")
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := NewTestUser("bob-oidc", "bob-oidc@example.org", "abcd", "user")
|
||||
require.NoError(t, err)
|
||||
user2.User.ExtID = new(uuid.NewSHA1(uuid.NameSpaceURL, []byte("https://example.org#bob-oidc")).String())
|
||||
require.NoError(t, user2.User.Save())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
info *oidc.UserInfo
|
||||
assert func(t *testing.T, user *users.User, err error)
|
||||
}{
|
||||
{
|
||||
name: "new user default group",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: "alice1",
|
||||
Username: "alice1",
|
||||
Email: "alice1@example.org",
|
||||
},
|
||||
assert: func(t *testing.T, user *users.User, err error) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "alice1", user.Username)
|
||||
assert.Equal(t, "alice1@example.org", user.Email)
|
||||
assert.Equal(t, "user", user.Group)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new user with oidc group",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: "alice2",
|
||||
Username: "alice2",
|
||||
Email: "alice2@example.org",
|
||||
Groups: []string{"roles:user", "roles:admin"},
|
||||
},
|
||||
assert: func(t *testing.T, user *users.User, err error) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "alice2", user.Username)
|
||||
assert.Equal(t, "alice2@example.org", user.Email)
|
||||
assert.Equal(t, "admin", user.Group)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new user with custom claims",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: app.Users["user"].User.Username,
|
||||
Username: app.Users["user"].User.Username,
|
||||
Email: app.Users["user"].User.Email,
|
||||
ReadeckUser: "new-user",
|
||||
ReadeckEmail: "user@example.net",
|
||||
ReadeckGroup: "admin",
|
||||
},
|
||||
assert: func(t *testing.T, user *users.User, err error) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "new-user", user.Username)
|
||||
assert.Equal(t, "user@example.net", user.Email)
|
||||
assert.Equal(t, "admin", user.Group)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "provision existing user",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: "bob",
|
||||
Username: "bob",
|
||||
Email: "bob-new-email@example.org",
|
||||
},
|
||||
assert: func(t *testing.T, user *users.User, err error) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bob", user.Username)
|
||||
assert.Equal(t, "bob-new-email@example.org", user.Email)
|
||||
assert.Equal(t, "user", user.Group)
|
||||
assert.Equal(t, user1.User.ID, user.ID)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update existing user",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: "bob-oidc",
|
||||
Username: "new-bob",
|
||||
Email: "bob@example.com",
|
||||
},
|
||||
assert: func(t *testing.T, user *users.User, err error) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "new-bob", user.Username)
|
||||
assert.Equal(t, "bob@example.com", user.Email)
|
||||
assert.Equal(t, "user", user.Group)
|
||||
assert.Equal(t, user2.User.ID, user.ID)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "conflicting information",
|
||||
info: &oidc.UserInfo{
|
||||
Subject: app.Users["admin"].User.Username,
|
||||
Username: app.Users["admin"].User.Username,
|
||||
Email: app.Users["user"].User.Email,
|
||||
},
|
||||
assert: func(t *testing.T, _ *users.User, err error) {
|
||||
require.Error(t, err, "more than one user is")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.info.Provider = providerInfo
|
||||
test.info.Issuer = providerInfo.URL
|
||||
|
||||
f := test.info.Form()
|
||||
require.True(t, f.IsValid())
|
||||
user, err := f.Execute()
|
||||
test.assert(t, user, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ package signin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"codeberg.org/readeck/readeck/components"
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
"codeberg.org/readeck/readeck/internal/auth/users"
|
||||
"codeberg.org/readeck/readeck/internal/server"
|
||||
@@ -36,7 +36,7 @@ func newAuthHandler(srv *server.Server) *authHandler {
|
||||
r.Use(
|
||||
server.Csrf,
|
||||
server.WithSession(),
|
||||
server.WithCustomErrorComponent(Views{}.Error),
|
||||
server.WithCustomErrorComponent(components.AuthError),
|
||||
)
|
||||
|
||||
h := &authHandler{r}
|
||||
@@ -62,24 +62,6 @@ func newAuthHandler(srv *server.Server) *authHandler {
|
||||
return h
|
||||
}
|
||||
|
||||
// Redirect triggers a 303 response with a redirect location, taking
|
||||
// care of not sending the user to the login page again.
|
||||
// Since it goes to [server.Redirect], it will be sanitized there
|
||||
// and can only stay within the app.
|
||||
func Redirect(w http.ResponseWriter, r *http.Request, redir string) {
|
||||
if redir == "" || strings.HasPrefix(redir, "/login") {
|
||||
redir = "/"
|
||||
}
|
||||
server.Redirect(w, r, redir)
|
||||
}
|
||||
|
||||
// RedirectToMFA triggers a 303 response to the MFA page, keeping
|
||||
// the initial redirect value.
|
||||
func RedirectToMFA(w http.ResponseWriter, r *http.Request, redir string) {
|
||||
v := url.Values{"r": {redir}}
|
||||
server.Redirect(w, r, "/login/mfa?"+v.Encode())
|
||||
}
|
||||
|
||||
func (h *authHandler) login(w http.ResponseWriter, r *http.Request) {
|
||||
f := forms.New[loginForm](r.Context())
|
||||
|
||||
@@ -90,10 +72,10 @@ func (h *authHandler) login(w http.ResponseWriter, r *http.Request) {
|
||||
// Do we have a session already?
|
||||
if sess := server.GetSession(r); sess.Payload.User != 0 {
|
||||
if sess.Payload.RequiresMFA {
|
||||
RedirectToMFA(w, r, f.Redirect.Value())
|
||||
server.RedirectToMFA(w, r, f.Redirect.Value())
|
||||
return
|
||||
}
|
||||
Redirect(w, r, f.Redirect.Value())
|
||||
server.RedirectNoLogin(w, r, f.Redirect.Value())
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -113,7 +95,7 @@ func (h *authHandler) login(w http.ResponseWriter, r *http.Request) {
|
||||
sess.Save(w, r)
|
||||
|
||||
if sess.Payload.RequiresMFA {
|
||||
RedirectToMFA(w, r, f.Redirect.Value())
|
||||
server.RedirectToMFA(w, r, f.Redirect.Value())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,7 +103,7 @@ func (h *authHandler) login(w http.ResponseWriter, r *http.Request) {
|
||||
"last_login": time.Now().UTC(),
|
||||
})
|
||||
|
||||
Redirect(w, r, f.Redirect.Value())
|
||||
server.RedirectNoLogin(w, r, f.Redirect.Value())
|
||||
return
|
||||
}
|
||||
// we must set the content type to avoid the
|
||||
|
||||
@@ -8,23 +8,17 @@ import (
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
|
||||
. "codeberg.org/readeck/readeck/components"
|
||||
F "codeberg.org/readeck/readeck/components/forms"
|
||||
"net/http"
|
||||
"codeberg.org/readeck/readeck/internal/auth/oidc"
|
||||
)
|
||||
|
||||
type Views struct{}
|
||||
|
||||
func SigninLayout(title string) templ.Component {
|
||||
return Views{}.base(title)
|
||||
}
|
||||
|
||||
type Components struct{}
|
||||
|
||||
templ (_ Components) OIDC() {
|
||||
if providers := configs.Config.Auth.OIDC.Providers; len(providers) > 0 {
|
||||
if providers := oidc.Providers.Items(); len(providers) > 0 {
|
||||
<form action={ URL(ctx, "/login/oidc") } method="post">
|
||||
<fieldset class="mt-8 border-t">
|
||||
<legend class="mx-auto px-2">{ L(ctx).Gettext("Or continue with") }</legend>
|
||||
@@ -46,41 +40,8 @@ templ (_ Components) OIDC() {
|
||||
}
|
||||
}
|
||||
|
||||
templ (_ Views) header() {
|
||||
<h1 class="max-w-sm mt-12 mx-auto px-8">
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 567 128" class="w-full fill-gray-900">
|
||||
<use href={ Asset(ctx, "img/logo-text.svg") + "#main" }></use>
|
||||
</svg>
|
||||
</div>
|
||||
</h1>
|
||||
}
|
||||
|
||||
templ (v Views) base(title string) {
|
||||
@BasePage(title) {
|
||||
@v.header()
|
||||
<main class="w-full max-w-sm p-8 mt-6 mx-auto bg-gray-100 text-gray-dark rounded-md shadow-md" id="content">
|
||||
{ children... }
|
||||
</main>
|
||||
@CustomTemplate("auth/footer.html.tmpl", nil)
|
||||
}
|
||||
}
|
||||
|
||||
templ (v Views) Error(status int) {
|
||||
@BasePage(L(ctx).Gettext("Error")) {
|
||||
@v.header()
|
||||
<main class="w-full max-w-sm p-8 mt-6 mx-auto text-center border border-red-800 bg-red-100 rounded-md" id="content">
|
||||
<h2 class="title text-h3">{ L(ctx).Gettext("An error occurred") }</h2>
|
||||
<p>{ http.StatusText(status) }</p>
|
||||
<p class="mt-8">
|
||||
<a class="link font-semibold" href={ URL(ctx, "/") }>{ L(ctx).Gettext("Go back to Readeck") }</a>
|
||||
</p>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
||||
templ (v Views) signIn(f *loginForm) {
|
||||
@v.base(L(ctx).Gettext("Sign in")) {
|
||||
@AuthLayout(L(ctx).Gettext("Sign in")) {
|
||||
<h2 class="text-h3 mb-8 text-center">{ L(ctx).Gettext("Sign in to Readeck") }</h2>
|
||||
<form
|
||||
action={ URL(ctx, "/login") }
|
||||
@@ -124,7 +85,7 @@ templ (v Views) signIn(f *loginForm) {
|
||||
|
||||
templ (v Views) mfa(f *totpForm) {
|
||||
{{ title := L(ctx).Gettext("Two-factor authentication") }}
|
||||
@v.base(title) {
|
||||
@AuthLayout(title) {
|
||||
<h2 class="text-h3 mb-8 text-center">{ title }</h2>
|
||||
<form action={ URL(ctx, "/login/mfa") } method="post">
|
||||
@F.Errors(f)
|
||||
@@ -159,7 +120,7 @@ templ (v Views) recover(f *recoverForm, err error) {
|
||||
title := L(ctx).Gettext("Password recovery")
|
||||
step := f.Step.Value()
|
||||
}}
|
||||
@v.base(title) {
|
||||
@AuthLayout(title) {
|
||||
<h2 class="text-h3 mb-8 text-center">{ title }</h2>
|
||||
switch step {
|
||||
case 0:
|
||||
|
||||
Generated
+207
-421
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,8 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/readeck/readeck/configs"
|
||||
"codeberg.org/readeck/readeck/internal/server/urls"
|
||||
@@ -147,3 +149,18 @@ func Redirect(w http.ResponseWriter, r *http.Request, ref ...string) {
|
||||
w.Header().Set("Location", urls.AbsoluteURL(r, ref...).String())
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// RedirectNoLogin yields a 303 redirection to "redir", unless it's a
|
||||
// redirection to the login page.
|
||||
func RedirectNoLogin(w http.ResponseWriter, r *http.Request, redir string) {
|
||||
if redir == "" || strings.HasPrefix(redir, "/login") {
|
||||
redir = "/"
|
||||
}
|
||||
Redirect(w, r, redir)
|
||||
}
|
||||
|
||||
// RedirectToMFA yields a 303 response to the MFA page, keeping
|
||||
// the initial redirect value.
|
||||
func RedirectToMFA(w http.ResponseWriter, r *http.Request, redir string) {
|
||||
Redirect(w, r, "/login/mfa?"+url.Values{"r": {redir}}.Encode())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user