Skip to content

Commit

Permalink
Add option for custom logos on the sign in page
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelSpeed committed Feb 19, 2021
1 parent ad2d7b1 commit 23e545a
Showing 10 changed files with 220 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@

## Changes since v7.0.1

- [#1056](https://github.com/oauth2-proxy/oauth2-proxy/pull/1056) Add option for custom logos on the sign in page (@JoelSpeed)
- [#1054](https://github.com/oauth2-proxy/oauth2-proxy/pull/1054) Update to Go 1.16 (@JoelSpeed)
- [#1052](https://github.com/oauth2-proxy/oauth2-proxy/pull/1052) Update golangci-lint to latest version (v1.36.0) (@JoelSpeed)
- [#1043](https://github.com/oauth2-proxy/oauth2-proxy/pull/1043) Refactor Sign In Page rendering and capture all page rendering code in pagewriter package (@JoelSpeed)
1 change: 1 addition & 0 deletions docs/docs/configuration/overview.md
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--cookie-secure` | bool | set [secure (HTTPS only) cookie flag](https://owasp.org/www-community/controls/SecureFlag) | true |
| `--cookie-samesite` | string | set SameSite cookie attribute (`"lax"`, `"strict"`, `"none"`, or `""`). | `""` |
| `--custom-templates-dir` | string | path to custom html templates | |
| `--custom-sign-in-logo` | string | path to an custom image for the sign_in page logo. Use \"-\" to disable default logo. |
| `--display-htpasswd-form` | bool | display username / password login form if an htpasswd file is provided | true |
| `--email-domain` | string \| list | authenticate emails with the specified domain (may be given multiple times). Use `*` to authenticate any email | |
| `--errors-to-info-log` | bool | redirects error-level logging to default log channel instead of stderr | |
1 change: 1 addition & 0 deletions oauthproxy.go
Original file line number Diff line number Diff line change
@@ -123,6 +123,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr

pageWriter, err := pagewriter.NewWriter(pagewriter.Opts{
TemplatesPath: opts.Templates.Path,
CustomLogo: opts.Templates.CustomLogo,
ProxyPrefix: opts.ProxyPrefix,
Footer: opts.Templates.Footer,
Version: VERSION,
7 changes: 7 additions & 0 deletions pkg/apis/options/app.go
Original file line number Diff line number Diff line change
@@ -11,6 +11,12 @@ type Templates struct {
// If either file is missing, the default will be used instead.
Path string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`

// CustomLogo is the path to a logo that should replace the default logo
// on the sign_in page template.
// Supported formats are .svg, .png, .jpg and .jpeg.
// To disable the default logo, set this value to "-".
CustomLogo string `flag:"custom-sign-in-logo" cfg:"custom_sign_in_logo"`

// Banner overides the default sign_in page banner text. If unspecified,
// the message will give users a list of allowed email domains.
Banner string `flag:"banner" cfg:"banner"`
@@ -34,6 +40,7 @@ func templatesFlagSet() *pflag.FlagSet {
flagSet := pflag.NewFlagSet("templates", pflag.ExitOnError)

flagSet.String("custom-templates-dir", "", "path to custom html templates")
flagSet.String("custom-sign-in-logo", "", "path to an custom image for the sign_in page logo. Use \"-\" to disable default logo.")
flagSet.String("banner", "", "custom banner string. Use \"-\" to disable default banner.")
flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")
flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
1 change: 1 addition & 0 deletions pkg/app/pagewriter/default_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions pkg/app/pagewriter/pagewriter.go
Original file line number Diff line number Diff line change
@@ -49,6 +49,10 @@ type Opts struct {

// SignInMessage is the messge displayed above the login button.
SignInMessage string

// CustomLogo is the path to a logo to be displayed on the sign in page.
// The logo can be either PNG, JPG/JPEG or SVG.
CustomLogo string
}

// NewWriter constructs a Writer from the options given to allow
@@ -59,6 +63,11 @@ func NewWriter(opts Opts) (Writer, error) {
return nil, fmt.Errorf("error loading templates: %v", err)
}

logoData, err := loadCustomLogo(opts.CustomLogo)
if err != nil {
return nil, fmt.Errorf("error loading logo: %v", err)
}

errorPage := &errorPageWriter{
template: templates.Lookup("error.html"),
proxyPrefix: opts.ProxyPrefix,
@@ -76,6 +85,7 @@ func NewWriter(opts Opts) (Writer, error) {
footer: opts.Footer,
version: opts.Version,
displayLoginForm: opts.DisplayLoginForm,
logoData: logoData,
}

return &pageWriter{
9 changes: 9 additions & 0 deletions pkg/app/pagewriter/sign_in.html
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@
max-width: 400px;
margin: 1.25rem auto;
}
.logo-box {
margin: 1.5rem 3rem;
}
footer a {
text-decoration: underline;
}
@@ -40,6 +43,12 @@
<body class="has-background-light">
<section class="section">
<div class="box block sign-in-box has-text-centered">
{{ if .LogoData }}
<div class="block logo-box">
{{.LogoData}}
</div>
{{ end }}

<form method="GET" action="{{.ProxyPrefix}}/start">
<input type="hidden" name="rd" value="{{.Redirect}}">
{{ if .SignInMessage }}
57 changes: 57 additions & 0 deletions pkg/app/pagewriter/sign_in_page.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package pagewriter

import (
// Import embed to allow importing default logo
_ "embed"

"encoding/base64"
"fmt"
"os"
"path/filepath"
"strings"

"html/template"
"net/http"

"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
)

//go:embed default_logo.svg
var defaultLogoData string

// signInPageWriter is used to render sign-in pages.
type signInPageWriter struct {
// Template is the sign-in page HTML template.
@@ -33,6 +45,10 @@ type signInPageWriter struct {

// DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
displayLoginForm bool

// LogoData is the logo to render in the template.
// This should contain valid html.
logoData string
}

// WriteSignInPage writes the sign-in page to the given response writer.
@@ -48,6 +64,7 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
Version string
ProxyPrefix string
Footer template.HTML
LogoData template.HTML
}{
ProviderName: s.providerName,
SignInMessage: template.HTML(s.signInMessage),
@@ -56,6 +73,7 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
Version: s.version,
ProxyPrefix: s.proxyPrefix,
Footer: template.HTML(s.footer),
LogoData: template.HTML(s.logoData),
}

err := s.template.Execute(rw, t)
@@ -64,3 +82,42 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
s.errorPageWriter.WriteErrorPage(rw, http.StatusInternalServerError, redirectURL, err.Error())
}
}

// loadCustomLogo loads the logo file from the path and encodes it to an HTML
// entity. If no custom logo is provided, the OAuth2 Proxy Icon is used instead.
func loadCustomLogo(logoPath string) (string, error) {
if logoPath == "" {
// The default logo is an SVG so this will be valid to just return.
return defaultLogoData, nil
}

if logoPath == "-" {
// Return no logo when the custom logo is set to `-`.
// This disables the logo rendering.
return "", nil
}

logoData, err := os.ReadFile(logoPath)
if err != nil {
return "", fmt.Errorf("could not read logo file: %v", err)
}

extension := strings.ToLower(filepath.Ext(logoPath))
switch extension {
case ".svg":
return string(logoData), nil
case ".jpg", ".jpeg":
return encodeImg(logoData, "jpeg"), nil
case ".png":
return encodeImg(logoData, "png"), nil
default:
return "", fmt.Errorf("unknown extension: %q, supported extensions are .svg, .jpg, .jpeg and .png", extension)
}
}

// encodeImg takes the raw image data and converts it to an HTML Img tag with
// a base64 data source.
func encodeImg(data []byte, format string) string {
b64Data := base64.StdEncoding.EncodeToString(data)
return fmt.Sprintf("<img src=\"data:image/%s;base64,%s\" alt=\"Logo\" />", format, b64Data)
}
Loading

0 comments on commit 23e545a

Please sign in to comment.