بنقرة واحدة
oidc-hosted-page-go
Implement "Sign in with SSO" in Go applications using SSOJet OIDC Authorization Code flow.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Implement "Sign in with SSO" in Go applications using SSOJet OIDC Authorization Code flow.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Implement Machine-to-Machine (M2M) authentication using SSOJet OAuth2 Client Credentials flow for backend services and daemons.
Implement "Sign in with SSO" in native Android/Kotlin applications using SSOJet OIDC with AppAuth.
Implement "Sign in with SSO" in Angular SPA applications using SSOJet OIDC with PKCE.
Implement "Sign in with SSO" in ASP.NET Core applications using SSOJet OIDC middleware.
Implement "Sign in with SSO" in native iOS/Swift applications using SSOJet OIDC with AppAuth.
Implement "Sign in with SSO" in Java Spring Boot applications using SSOJet OIDC with Spring Security.
| name | oidc-hosted-page-go |
| description | Implement "Sign in with SSO" in Go applications using SSOJet OIDC Authorization Code flow. |
This expert AI assistant guide walks you through integrating "Sign in with SSO" functionality into an existing login page in a Go application using SSOJet as an OIDC identity provider. The goal is to modify the existing login flow to add SSO support without disrupting the current traditional login functionality (e.g., email/password).
net/http or a web framework like chi or gorilla/mux.github.com/coreos/go-oidc/v3/oidc, golang.org/x/oauth2.http://localhost:8080/auth/callback).Run the following commands to install the required packages:
go get github.com/coreos/go-oidc/v3/oidc
go get golang.org/x/oauth2
Set the following environment variables (or use a .env loader like godotenv):
SSOJET_ISSUER_URL=https://auth.ssojet.com
SSOJET_CLIENT_ID=your_client_id
SSOJET_CLIENT_SECRET=your_client_secret
SSOJET_REDIRECT_URI=http://localhost:8080/auth/callback
Create a dedicated file for OIDC configuration (e.g., internal/auth/oidc.go):
// internal/auth/oidc.go
package auth
import (
"context"
"os"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
var (
OIDCProvider *oidc.Provider
OAuth2Config oauth2.Config
)
func InitOIDC() error {
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, os.Getenv("SSOJET_ISSUER_URL"))
if err != nil {
return err
}
OIDCProvider = provider
OAuth2Config = oauth2.Config{
ClientID: os.Getenv("SSOJET_CLIENT_ID"),
ClientSecret: os.Getenv("SSOJET_CLIENT_SECRET"),
RedirectURL: os.Getenv("SSOJET_REDIRECT_URI"),
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
return nil
}
Create or modify your login page template (e.g., templates/login.html):
<!-- templates/login.html -->
<!DOCTYPE html>
<html>
<head><title>Sign In</title></head>
<body>
<div class="login-container">
<h1>Sign In</h1>
{{if .Error}}
<p style="color: red;">{{.Error}}</p>
{{end}}
<form id="loginForm" method="POST" action="/auth/login">
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div id="passwordField">
<label for="password">Password</label>
<input type="password" id="password" name="password" required />
</div>
<input type="hidden" id="isSSO" name="is_sso" value="false" />
<button type="submit" id="submitBtn">Sign In</button>
</form>
<button type="button" id="ssoToggle" onclick="toggleSSO()">
Sign in with SSO
</button>
</div>
<script>
function toggleSSO() {
const isSSO = document.getElementById('isSSO');
const passwordField = document.getElementById('passwordField');
const submitBtn = document.getElementById('submitBtn');
const ssoToggle = document.getElementById('ssoToggle');
if (isSSO.value === 'false') {
isSSO.value = 'true';
passwordField.style.display = 'none';
document.getElementById('password').removeAttribute('required');
submitBtn.textContent = 'Continue with SSO';
ssoToggle.textContent = 'Back to Password Login';
} else {
isSSO.value = 'false';
passwordField.style.display = 'block';
document.getElementById('password').setAttribute('required', 'true');
submitBtn.textContent = 'Sign In';
ssoToggle.textContent = 'Sign in with SSO';
}
}
</script>
</body>
</html>
Create the necessary handlers to process the OIDC flow.
1. Login Handler (internal/auth/handlers.go):
// internal/auth/handlers.go
package auth
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"log"
"net/http"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
func generateState() string {
b := make([]byte, 16)
rand.Read(b)
return base64.URLEncoding.EncodeToString(b)
}
// LoginHandler handles the login form submission.
func LoginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
r.ParseForm()
email := r.FormValue("email")
isSSO := r.FormValue("is_sso")
if isSSO == "true" {
// Generate a random state for CSRF protection
state := generateState()
// Store state in a cookie
http.SetCookie(w, &http.Cookie{
Name: "oidc_state",
Value: state,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
MaxAge: 3600,
})
// Build authorization URL with login_hint
authURL := OAuth2Config.AuthCodeURL(state,
oauth2.SetAuthURLParam("login_hint", email),
)
http.Redirect(w, r, authURL, http.StatusFound)
return
}
// Existing password login logic here
log.Println("Processing traditional login...")
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
2. Callback Handler (add to internal/auth/handlers.go):
// CallbackHandler handles the OIDC callback.
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
// Retrieve stored state from cookie
stateCookie, err := r.Cookie("oidc_state")
if err != nil {
log.Println("State cookie not found:", err)
http.Redirect(w, r, "/login?error=state_missing", http.StatusFound)
return
}
// Verify state
if r.URL.Query().Get("state") != stateCookie.Value {
log.Println("State mismatch")
http.Redirect(w, r, "/login?error=state_mismatch", http.StatusFound)
return
}
// Exchange authorization code for token
code := r.URL.Query().Get("code")
token, err := OAuth2Config.Exchange(r.Context(), code)
if err != nil {
log.Println("Token exchange failed:", err)
http.Redirect(w, r, "/login?error=token_exchange_failed", http.StatusFound)
return
}
// Extract and verify ID token
rawIDToken, ok := token.Extra("id_token").(string)
if !ok {
log.Println("No id_token in response")
http.Redirect(w, r, "/login?error=no_id_token", http.StatusFound)
return
}
verifier := OIDCProvider.Verifier(&oidc.Config{ClientID: OAuth2Config.ClientID})
idToken, err := verifier.Verify(r.Context(), rawIDToken)
if err != nil {
log.Println("ID token verification failed:", err)
http.Redirect(w, r, "/login?error=token_verification_failed", http.StatusFound)
return
}
// Extract user claims
var claims map[string]interface{}
if err := idToken.Claims(&claims); err != nil {
log.Println("Failed to parse claims:", err)
http.Redirect(w, r, "/login?error=claims_parse_failed", http.StatusFound)
return
}
// Clear the state cookie
http.SetCookie(w, &http.Cookie{
Name: "oidc_state",
Value: "",
Path: "/",
MaxAge: -1,
})
// TODO: Create a session for the user based on claims
claimsJSON, _ := json.Marshal(claims)
http.SetCookie(w, &http.Cookie{
Name: "user_session",
Value: base64.URLEncoding.EncodeToString(claimsJSON),
Path: "/",
HttpOnly: true,
MaxAge: 3600,
})
log.Println("Authenticated User:", claims)
// Redirect to the dashboard or intended page
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
3. Main Application Setup (main.go):
// main.go
package main
import (
"log"
"net/http"
"html/template"
"yourmodule/internal/auth"
)
func main() {
// Initialize OIDC
if err := auth.InitOIDC(); err != nil {
log.Fatal("Failed to initialize OIDC:", err)
}
// Routes
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("templates/login.html"))
data := map[string]string{"Error": r.URL.Query().Get("error")}
tmpl.Execute(w, data)
})
http.HandleFunc("/auth/login", auth.LoginHandler)
http.HandleFunc("/auth/callback", auth.CallbackHandler)
http.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<h1>Dashboard</h1><p>Welcome!</p>"))
})
log.Println("Server running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
go run main.go.http://localhost:8080/login)./auth/callback and then to /dashboard.gorilla/sessions) instead of raw cookies in production.godotenv for local development and proper secrets management in production.