Improve token handling

- add identity information to the index page
- let the session expire when the token expires
This commit is contained in:
Jan Dittberner 2023-07-30 16:48:26 +02:00
parent bc35b0984f
commit 9ad06a2935
10 changed files with 53 additions and 11 deletions

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- let the session expire when the token expires
### Added
- add identity output to index page
## [0.2.0]
### Changed
- re-order configuration precedence

View file

@ -128,7 +128,7 @@ func main() {
publicURL := buildPublicURL(config.MustString("server.name"), config.MustInt("server.port"))
indexHandler, err := handlers.NewIndexHandler(bundle, catalog, ui.Templates, oidcInfo, publicURL)
indexHandler, err := handlers.NewIndexHandler(logger, bundle, catalog, oidcInfo, publicURL)
if err != nil {
logger.WithError(err).Fatal("could not initialize index handler")
}
@ -154,7 +154,7 @@ func main() {
logging := handlers.Logging(logger)
hsts := handlers.EnableHSTS()
errorMiddleware, err := handlers.ErrorHandling(logger, ui.Templates, bundle, catalog)
errorMiddleware, err := handlers.ErrorHandling(logger, bundle, catalog)
if err != nil {
logger.WithError(err).Fatal("could not initialize request error handling")
}

View file

@ -77,7 +77,7 @@ func Authenticate(logger *log.Logger, oauth2Config *oauth2.Config, clientID stri
queryValues := authURL.Query()
queryValues.Set("client_id", clientID)
queryValues.Set("response_type", "code")
queryValues.Set("scope", "openid offline_access profile email")
queryValues.Set("scope", "openid profile email cacert_groups")
queryValues.Set("state", base64.URLEncoding.EncodeToString(services.GenerateKey(oauth2RedirectStateLength)))
queryValues.Set("claims", getRequestedClaims(logger))
authURL.RawQuery = queryValues.Encode()

View file

@ -21,12 +21,13 @@ import (
"context"
"fmt"
"html/template"
"io/fs"
"net/http"
"github.com/nicksnyder/go-i18n/v2/i18n"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-demo-app/ui"
"code.cacert.org/cacert/oidc-demo-app/internal/services"
)
@ -135,12 +136,11 @@ func (w *errorResponseWriter) Write(content []byte) (int, error) {
func ErrorHandling(
logger *log.Logger,
templateFS fs.FS,
bundle *i18n.Bundle,
messageCatalog *services.MessageCatalog,
) (func(http.Handler) http.Handler, error) {
errorTemplates, err := template.ParseFS(
templateFS,
ui.Templates,
"templates/base.gohtml",
"templates/errors.gohtml",
)

View file

@ -20,12 +20,14 @@ package handlers
import (
"fmt"
"html/template"
"io/fs"
"net/http"
"net/url"
"github.com/lestrrat-go/jwx/jwk"
"github.com/nicksnyder/go-i18n/v2/i18n"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-demo-app/ui"
"code.cacert.org/cacert/oidc-demo-app/internal/services"
)
@ -33,6 +35,7 @@ import (
type IndexHandler struct {
bundle *i18n.Bundle
indexTemplate *template.Template
logger *log.Logger
keySet jwk.Set
logoutURL string
messageCatalog *services.MessageCatalog
@ -72,10 +75,20 @@ func (h *IndexHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reque
}
var (
idToken string
ok bool
accessToken string
refreshToken string
idToken string
ok bool
)
if accessToken, ok = session.Values[sessionKeyAccessToken].(string); ok {
h.logger.WithField("access_token", accessToken).Info("found access token in session")
}
if refreshToken, ok = session.Values[refreshToken].(string); ok {
h.logger.WithField("refresh_token", refreshToken).Info("found refresh token in session")
}
if idToken, ok = session.Values[sessionKeyIDToken].(string); ok {
logoutURL.RawQuery = url.Values{
"id_token_hint": []string{idToken},
@ -92,6 +105,10 @@ func (h *IndexHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reque
return
}
expires := oidcToken.Expiration()
h.logger.WithField("expires", expires).Info("id token expires at")
writer.Header().Add("Content-Type", "text/html")
msgLookup := h.messageCatalog.LookupMessage
@ -104,6 +121,10 @@ func (h *IndexHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reque
"IntroductionText": msgLookup("IndexIntroductionText", nil, localizer),
"LogoutLabel": msgLookup("LogoutLabel", nil, localizer),
"LogoutURL": logoutURL.String(),
"AuthenticatedAs": msgLookup("AuthenticatedAs", map[string]interface{}{
"Name": oidcToken.Name(),
"Email": oidcToken.Email(),
}, localizer),
})
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
@ -113,20 +134,21 @@ func (h *IndexHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reque
}
func NewIndexHandler(
logger *log.Logger,
bundle *i18n.Bundle,
catalog *services.MessageCatalog,
templateFS fs.FS,
oidcInfo *services.OIDCInformation,
publicURL string,
) (*IndexHandler, error) {
indexTemplate, err := template.ParseFS(
templateFS,
ui.Templates,
"templates/base.gohtml", "templates/index.gohtml")
if err != nil {
return nil, fmt.Errorf("could not parse templates: %w", err)
}
return &IndexHandler{
logger: logger,
bundle: bundle,
indexTemplate: indexTemplate,
keySet: oidcInfo.KeySet,

View file

@ -20,6 +20,7 @@ package handlers
import (
"fmt"
"net/http"
"time"
"github.com/gorilla/sessions"
"github.com/lestrrat-go/jwx/jwk"
@ -142,6 +143,8 @@ func (c *OidcCallbackHandler) storeTokens(
session.Values[sessionKeyIDToken] = idToken
session.Options.MaxAge = int(time.Until(tok.Expiry).Seconds())
oidcToken, err := ParseIDToken(idToken, c.keySet)
if err != nil {
return fmt.Errorf("could not parse ID token: %w", err)

View file

@ -32,6 +32,11 @@ import (
func AddMessages(catalog *MessageCatalog) {
messages := make(map[string]*i18n.Message)
messages["AuthenticatedAs"] = &i18n.Message{
ID: "AuthenticatedAs",
Other: "The identity provider authenticated your identity as {{ .Name }}" +
" with the email address {{ .Email }}.",
}
messages["IndexGreeting"] = &i18n.Message{
ID: "IndexGreeting",
Other: "Hello {{ .User }}",

View file

@ -1,3 +1,7 @@
[AuthenticatedAs]
hash = "sha1-58e33592c806dab9cddd3693c0cfee64a07a0a9b"
other = "Der Identity-Provider hat dich als {{ .Name }} mit der E-Mail-Adresse {{ .Email }} identifiziert."
[ErrorTitle]
hash = "sha1-736aec25a98f5ec5b71400bb0163f891f509b566"
other = "Es ist ein Fehler aufgetreten"

View file

@ -1,3 +1,4 @@
AuthenticatedAs = "The identity provider authenticated your identity as {{ .Name }} with the email address {{ .Email }}."
ErrorTitle = "An error has occurred"
IndexGreeting = "Hello {{ .User }}"
IndexIntroductionText = "This is an authorization protected resource"

View file

@ -3,6 +3,7 @@
<img src="/images/CAcert-logo.svg" width="300" height="68" alt="CAcert" class="mb-4">
<h1>{{ .Greeting }}</h1>
<p>{{ .IntroductionText }}</p>
<p>{{ .AuthenticatedAs }}</p>
<a class="btn btn-outline-primary" href="{{ .LogoutURL }}">{{ .LogoutLabel }}</a>
</div>
{{ end }}