Skip to content

Instantly share code, notes, and snippets.

@gmlewis
Last active June 19, 2023 16:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmlewis/26ddbb2f90eb563415f19b9e13f0c8bb to your computer and use it in GitHub Desktop.
Save gmlewis/26ddbb2f90eb563415f19b9e13f0c8bb to your computer and use it in GitHub Desktop.
Mechanism to perform OAuth2 dance (e.g. with Keycloak) in a wails production build
// Copyright (C) 2023 Omics Data Automation, Inc. - All Rights Reserved
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"io"
"log"
"net/http"
"strings"
)
const (
port = ":5173"
)
// This callback server is started in a goroutine for serving
// the files from the "assets" embed.FS in "main.go".
//
// It is only started in a production build by checking on startup.
// For example, main.go looks like this:
//
// // The following are provided by injection from -ldflags:
// var (
// Standalone string // cannot be bool for injection to work
// )
//
// func main() {
// if Standalone == "true" {
// go startCallbackServer()
// }
// ...
// }
func startCallbackServer() {
http.HandleFunc("/", jwtHandler)
srv := &http.Server{Addr: port}
// always returns error. ErrServerClosed on graceful close
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("ListenAndServe: %v", err)
}
}
// jwtHandler serves the app files in order to handle redirects
// from OAuth2 dances (which the app itself then handles).
// The `injectEnvVars` function used below simply returns a string
// that embeds JavaScript before the rest of the file it is serving.
// A string like `window.configs = { "ENV_VAR_1": "value1", ...};`
// is an easy way to inject env vars into the app. Note that
// the trailing ';' is critical for not messing up the rest of the
// JavaScript file.
func jwtHandler(w http.ResponseWriter, r *http.Request) {
// log.Printf("jwtHandler: %v %v", r.Method, r.URL.Path)
path := r.URL.Path
if path == "/" {
// log.Printf("query: %#v", r.URL.Query())
path = "/index.html"
}
var mimeType string
switch {
case strings.HasSuffix(path, ".css"):
mimeType = "text/css"
case strings.HasSuffix(path, ".html"):
mimeType = "text/html"
case strings.HasSuffix(path, ".js"):
mimeType = "text/javascript"
case strings.HasSuffix(path, ".ttf"):
mimeType = "font/ttf"
case strings.HasSuffix(path, ".ico"):
mimeType = "image/vnd.microsoft.icon"
default:
log.Printf("Unknown MIME type for path %q", path)
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("not found"))
return
}
body, err := assets.Open("frontend/dist" + path)
if err != nil {
log.Printf("Unable to open %q: %v", path, err)
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("not found"))
return
}
defer body.Close()
w.Header().Add("Content-Type", mimeType)
if mimeType == "text/javascript" { // This must happen after the header is added.
if _, err := w.Write([]byte(injectEnvVars())); err != nil {
log.Printf("ERROR: Unable to inject env vars: %v", err)
}
}
if _, err := io.Copy(w, body); err != nil {
log.Printf("ERROR: io.Copy: %v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment