diff --git a/internal/web/auth.go b/internal/web/auth.go index f7d1b15..c33b8d7 100644 --- a/internal/web/auth.go +++ b/internal/web/auth.go @@ -462,7 +462,7 @@ func finishWebAuthnBinding(ctx echo.Context) error { saveSession(sess, ctx) addFlash(ctx, tr(ctx, "flash.auth.passkey-registred", passkeyName), "success") - return json(ctx, 200, []string{"OK"}) + return json(ctx, []string{"OK"}) } func beginWebAuthnLogin(ctx echo.Context) error { @@ -476,7 +476,7 @@ func beginWebAuthnLogin(ctx echo.Context) error { sess.Options.MaxAge = 5 * 60 // 5 minutes saveSession(sess, ctx) - return json(ctx, 200, credsCreation) + return json(ctx, credsCreation) } func finishWebAuthnLogin(ctx echo.Context) error { @@ -497,7 +497,7 @@ func finishWebAuthnLogin(ctx echo.Context) error { delete(sess.Values, "webauthn_login_session") saveSession(sess, ctx) - return json(ctx, 200, []string{"OK"}) + return json(ctx, []string{"OK"}) } func beginWebAuthnAssertion(ctx echo.Context) error { @@ -517,7 +517,7 @@ func beginWebAuthnAssertion(ctx echo.Context) error { sess.Options.MaxAge = 5 * 60 // 5 minutes saveSession(sess, ctx) - return json(ctx, 200, credsCreation) + return json(ctx, credsCreation) } func finishWebAuthnAssertion(ctx echo.Context) error { @@ -545,7 +545,7 @@ func finishWebAuthnAssertion(ctx echo.Context) error { delete(sess.Values, "mfaID") saveSession(sess, ctx) - return json(ctx, 200, []string{"OK"}) + return json(ctx, []string{"OK"}) } func beginTotp(ctx echo.Context) error { diff --git a/internal/web/server.go b/internal/web/server.go index fbaeb12..74530a5 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -218,14 +218,18 @@ func NewServer(isDev bool, sessionsPath string) *Server { } e.HTTPErrorHandler = func(er error, ctx echo.Context) { - if httpErr, ok := er.(*HTMLError); ok { + var httpErr *echo.HTTPError + if errors.As(er, &httpErr) { + acceptJson := strings.Contains(ctx.Request().Header.Get("Accept"), "application/json") setData(ctx, "error", er) - if fatalErr := htmlWithCode(ctx, httpErr.Code, "error.html"); fatalErr != nil { - log.Fatal().Err(fatalErr).Send() - } - } else if httpErr, ok := er.(*JSONError); ok { - if fatalErr := json(ctx, httpErr.Code, httpErr); fatalErr != nil { - log.Fatal().Err(fatalErr).Send() + if acceptJson { + if fatalErr := jsonWithCode(ctx, httpErr.Code, httpErr); fatalErr != nil { + log.Fatal().Err(fatalErr).Send() + } + } else { + if fatalErr := htmlWithCode(ctx, httpErr.Code, "error.html"); fatalErr != nil { + log.Fatal().Err(fatalErr).Send() + } } } else { log.Fatal().Err(er).Send() diff --git a/internal/web/util.go b/internal/web/util.go index 11d75b5..d0adc7b 100644 --- a/internal/web/util.go +++ b/internal/web/util.go @@ -19,14 +19,6 @@ import ( type dataTypeKey string -type HTMLError struct { - *echo.HTTPError -} - -type JSONError struct { - *echo.HTTPError -} - const dataKey dataTypeKey = "data" func setData(ctx echo.Context, key string, value any) { @@ -54,7 +46,11 @@ func htmlWithCode(ctx echo.Context, code int, template string) error { return ctx.Render(code, template, ctx.Request().Context().Value(dataKey)) } -func json(ctx echo.Context, code int, data any) error { +func json(ctx echo.Context, data any) error { + return jsonWithCode(ctx, 200, data) +} + +func jsonWithCode(ctx echo.Context, code int, data any) error { return ctx.JSON(code, data) } @@ -76,7 +72,7 @@ func errorRes(code int, message string, err error) error { skipLogger.Error().Err(err).Msg(message) } - return &HTMLError{&echo.HTTPError{Code: code, Message: message, Internal: err}} + return &echo.HTTPError{Code: code, Message: message, Internal: err} } func jsonErrorRes(code int, message string, err error) error { @@ -85,7 +81,7 @@ func jsonErrorRes(code int, message string, err error) error { skipLogger.Error().Err(err).Msg(message) } - return &JSONError{&echo.HTTPError{Code: code, Message: message, Internal: err}} + return &echo.HTTPError{Code: code, Message: message, Internal: err} } func getUserLogged(ctx echo.Context) *db.User { diff --git a/public/webauthn.ts b/public/webauthn.ts index 1517735..13b7ed0 100644 --- a/public/webauthn.ts +++ b/public/webauthn.ts @@ -22,6 +22,8 @@ function decodeBase64UrlToArrayBuffer(base64Url) { } async function bindPasskey() { + // @ts-ignore + const baseUrl = window.opengist_base_url || ''; let waitText = document.getElementById("login-passkey-wait"); try { @@ -30,7 +32,10 @@ async function bindPasskey() { let csrf = document.querySelector('form#webauthn input[name="_csrf"]').value - const beginResponse = await fetch('/webauthn/bind', { + const beginResponse = await fetch(`${baseUrl}/webauthn/bind`, { + headers: { + 'Accept': 'application/json', + }, method: 'POST', credentials: 'include', body: new FormData(document.querySelector('form#webauthn')) @@ -52,10 +57,11 @@ async function bindPasskey() { throw new Error('Credential object is missing required properties'); } - const finishResponse = await fetch('/webauthn/bind/finish', { + const finishResponse = await fetch(`${baseUrl}/webauthn/bind/finish`, { method: 'POST', credentials: 'include', headers: { + 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-CSRF-Token': csrf }, @@ -84,6 +90,8 @@ async function bindPasskey() { } async function loginWithPasskey() { + // @ts-ignore + const baseUrl = window.opengist_base_url || ''; let waitText = document.getElementById("login-passkey-wait"); try { @@ -91,7 +99,10 @@ async function loginWithPasskey() { waitText.classList.remove('hidden'); let csrf = document.querySelector('form#webauthn input[name="_csrf"]').value - const beginResponse = await fetch('/webauthn/' + loginMethod, { + const beginResponse = await fetch(`${baseUrl}/webauthn/${loginMethod}`, { + headers: { + 'Accept': 'application/json', + }, method: 'POST', credentials: 'include', body: new FormData(document.querySelector('form#webauthn')) @@ -115,10 +126,11 @@ async function loginWithPasskey() { throw new Error('Credential object is missing required properties'); } - const finishResponse = await fetch('/webauthn/' + loginMethod + '/finish', { + const finishResponse = await fetch(`${baseUrl}/webauthn/${loginMethod}/finish`, { method: 'POST', credentials: 'include', headers: { + 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-CSRF-Token': csrf },