Created
February 18, 2025 13:48
-
-
Save sarathsp06/6e59543c249bb44bfa851e73d4b1ffa4 to your computer and use it in GitHub Desktop.
OIDC cli sample
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"crypto/rand" | |
"encoding/base64" | |
"encoding/json" | |
"fmt" | |
"log" | |
"net/http" | |
"os" | |
"os/exec" | |
"runtime" | |
"time" | |
"github.com/joho/godotenv" | |
"golang.org/x/oauth2" | |
) | |
type OpenIDConfig struct { | |
Issuer string `json:"issuer"` | |
AuthorizationEndpoint string `json:"authorization_endpoint"` | |
TokenEndpoint string `json:"token_endpoint"` | |
JWKSURI string `json:"jwks_uri"` | |
ResponseTypesSupported []string `json:"response_types_supported"` | |
SubjectTypesSupported []string `json:"subject_types_supported"` | |
IDTokenSigningAlgValues []string `json:"id_token_signing_alg_values_supported"` | |
} | |
// 1. Fetch OpenID configuration | |
func fetchOpenIDConfig(configURL string) (*OpenIDConfig, error) { | |
resp, err := http.Get(configURL) | |
if err != nil { | |
return nil, err | |
} | |
defer resp.Body.Close() | |
var config OpenIDConfig | |
if err := json.NewDecoder(resp.Body).Decode(&config); err != nil { | |
return nil, err | |
} | |
return &config, nil | |
} | |
// 2. Create OAuth2 config and start authentication flow | |
func setupAuth(config *OpenIDConfig) *oauth2.Config { | |
port := getEnvOrDefault("SERVER_PORT", "8089") | |
callbackPath := getEnvOrDefault("CALLBACK_PATH", "/lp/whole-health-rx/glp1-weightloss-questionnaire/auth/callback") | |
redirectURL := fmt.Sprintf("http://localhost:%s%s", port, callbackPath) | |
return &oauth2.Config{ | |
ClientID: os.Getenv("OAUTH_CLIENT_ID"), | |
ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"), | |
RedirectURL: redirectURL, | |
Endpoint: oauth2.Endpoint{ | |
AuthURL: config.AuthorizationEndpoint, | |
TokenURL: config.TokenEndpoint, | |
}, | |
Scopes: []string{"openid", "offline_access"}, | |
} | |
} | |
func generateSecureState() (string, error) { | |
b := make([]byte, 16) | |
_, err := rand.Read(b) | |
if err != nil { | |
return "", err | |
} | |
return base64.URLEncoding.EncodeToString(b), nil | |
} | |
func openBrowser(url string) error { | |
var cmd *exec.Cmd | |
switch runtime.GOOS { | |
case "linux": | |
cmd = exec.Command("xdg-open", url) | |
case "windows": | |
cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", url) | |
case "darwin": | |
cmd = exec.Command("open", url) | |
default: | |
return fmt.Errorf("unsupported platform") | |
} | |
return cmd.Start() | |
} | |
func getEnvOrDefault(key, defaultValue string) string { | |
value := os.Getenv(key) | |
if value == "" { | |
return defaultValue | |
} | |
return value | |
} | |
func main() { | |
// Load environment variables from .env file if it exists | |
_ = godotenv.Load() | |
// Fetch OpenID configuration | |
configURL := getEnvOrDefault("OPENID_CONFIG_URL", | |
"https://patient.careconnect.stg.wellsync.io/.well-known/openid-configuration") | |
config, err := fetchOpenIDConfig(configURL) | |
if err != nil { | |
log.Fatalf("Failed to fetch OpenID config: %v", err) | |
} | |
// Generate secure state | |
state, err := generateSecureState() | |
if err != nil { | |
log.Fatalf("Failed to generate state: %v", err) | |
} | |
// Setup OAuth2 config | |
oauth2Config := setupAuth(config) | |
// Start local server to handle callback | |
var token *oauth2.Token | |
var serverErr error | |
port := getEnvOrDefault("SERVER_PORT", "8089") | |
server := &http.Server{Addr: ":" + port} | |
callbackPath := getEnvOrDefault("CALLBACK_PATH", | |
"/lp/whole-health-rx/glp1-weightloss-questionnaire/auth/callback") | |
http.HandleFunc(callbackPath, func(w http.ResponseWriter, r *http.Request) { | |
if r.URL.Query().Get("state") != state { | |
serverErr = fmt.Errorf("state mismatch") | |
http.Error(w, "State mismatch", http.StatusBadRequest) | |
return | |
} | |
code := r.URL.Query().Get("code") | |
if code == "" { | |
serverErr = fmt.Errorf("no code returned") | |
http.Error(w, "No code returned", http.StatusBadRequest) | |
return | |
} | |
ctx := context.Background() | |
token, err = oauth2Config.Exchange(ctx, code) | |
if err != nil { | |
serverErr = fmt.Errorf("failed to exchange token: %v", err) | |
http.Error(w, "Failed to exchange token", http.StatusInternalServerError) | |
return | |
} | |
w.Write([]byte("Authentication successful! You can close this window.")) | |
// Shutdown server after a short delay | |
go func() { | |
time.Sleep(100 * time.Millisecond) | |
server.Shutdown(ctx) | |
}() | |
}) | |
// Start server in a goroutine | |
go func() { | |
if err := server.ListenAndServe(); err != http.ErrServerClosed { | |
log.Printf("HTTP server error: %v", err) | |
} | |
}() | |
// Get authorization URL and open browser | |
authURL := oauth2Config.AuthCodeURL(state, oauth2.AccessTypeOffline) | |
fmt.Printf("Opening browser to authenticate: %s\n", authURL) | |
if err := openBrowser(authURL); err != nil { | |
fmt.Printf("Failed to open browser. Please open this URL manually: %s\n", authURL) | |
} | |
// Wait for server to shut down | |
var v string | |
fmt.Scanln(&v) | |
if serverErr != nil { | |
log.Fatalf("Authentication failed: %v", serverErr) | |
} | |
if token == nil { | |
log.Fatalf("No token received") | |
} | |
// Print the token | |
tokenJSON, _ := json.MarshalIndent(token, "", " ") | |
fmt.Printf("Token received:\n%s\n", tokenJSON) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment