mirror of
https://github.com/Dvorinka/excalidraw-full.git
synced 2026-06-03 22:02:57 +00:00
添加 OIDC 认证支持,更新环境变量配置,重构 Docker Compose 文件,移除旧的 Dex 初始化脚本,优化用户模型,更新前端登录流程,支持通过 OIDC 登录。
This commit is contained in:
+13
-1
@@ -1,8 +1,20 @@
|
|||||||
|
# GitHub OAuth 配置
|
||||||
GITHUB_CLIENT_ID="xxxxxxxxxxxxxxxxxxx"
|
GITHUB_CLIENT_ID="xxxxxxxxxxxxxxxxxxx"
|
||||||
GITHUB_CLIENT_SECRET="xxxxxxxxxxxxxx"
|
GITHUB_CLIENT_SECRET="xxxxxxxxxxxxxx"
|
||||||
GITHUB_REDIRECT_URL="http://localhost:3000/auth/github/callback" # 或者你部署后的回调地址
|
GITHUB_REDIRECT_URL="http://localhost:3002/auth/github/callback" # 或者你部署后的回调地址
|
||||||
|
|
||||||
|
# OIDC 配置
|
||||||
|
OIDC_ISSUER_URL="http://localhost:5556/.well-known/openid-configuration" # OIDC 提供商地址
|
||||||
|
OIDC_CLIENT_ID="excalidraw"
|
||||||
|
OIDC_CLIENT_SECRET="excalidraw-secret"
|
||||||
|
OIDC_REDIRECT_URL="http://localhost:3002/auth/oidc/callback"
|
||||||
|
|
||||||
|
# JWT 配置
|
||||||
JWT_SECRET="YOUR_SUPER_SECRET_RANDOM_STRING"
|
JWT_SECRET="YOUR_SUPER_SECRET_RANDOM_STRING"
|
||||||
|
|
||||||
|
# OpenAI 配置
|
||||||
OPENAI_API_KEY=sk-xxxxxxxxxxxxxx
|
OPENAI_API_KEY=sk-xxxxxxxxxxxxxx
|
||||||
OPENAI_BASE_URL=https://xxxxxx.xxxxx # 不加/v1...
|
OPENAI_BASE_URL=https://xxxxxx.xxxxx # 不加/v1...
|
||||||
|
|
||||||
|
# 存储配置
|
||||||
STORAGE_TYPE=sqlite # 支持memory,filesystem, kv, s3,具体看README
|
STORAGE_TYPE=sqlite # 支持memory,filesystem, kv, s3,具体看README
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
issuer: http://localhost:5556
|
||||||
|
|
||||||
|
storage:
|
||||||
|
type: memory
|
||||||
|
|
||||||
|
web:
|
||||||
|
http: 0.0.0.0:5556
|
||||||
|
allowedOrigins: ["*"]
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: debug
|
||||||
|
format: text
|
||||||
|
|
||||||
|
enablePasswordDB: true
|
||||||
|
|
||||||
|
staticClients:
|
||||||
|
- id: excalidraw
|
||||||
|
redirectURIs:
|
||||||
|
- {{ .Env.OIDC_REDIRECT_URL }}
|
||||||
|
name: Excalidraw
|
||||||
|
public: true
|
||||||
|
secret: excalidraw-secret
|
||||||
|
|
||||||
|
staticPasswords:
|
||||||
|
- email: {{ .Env.ADMIN_EMAIL }}
|
||||||
|
hash: {{ .Env.ADMIN_PASSWORD_HASH }}
|
||||||
|
username: {{ .Env.ADMIN_USERNAME }}
|
||||||
|
userID: {{ .Env.ADMIN_USER_ID }}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
issuer: http://localhost:5556
|
|
||||||
|
|
||||||
storage:
|
|
||||||
type: sqlite3
|
|
||||||
config:
|
|
||||||
file: /var/lib/dex/dex.db
|
|
||||||
|
|
||||||
web:
|
|
||||||
http: 0.0.0.0:5556
|
|
||||||
allowedOrigins: ["*"]
|
|
||||||
|
|
||||||
logger:
|
|
||||||
level: debug
|
|
||||||
format: text
|
|
||||||
|
|
||||||
enablePasswordDB: true
|
|
||||||
|
|
||||||
staticClients:
|
|
||||||
- id: excalidraw
|
|
||||||
redirectURIs:
|
|
||||||
- http://localhost:3002/auth/oidc/callback
|
|
||||||
name: Excalidraw
|
|
||||||
secret: ${OIDC_CLIENT_SECRET:-excalidraw-secret}
|
|
||||||
|
|
||||||
staticPasswords:
|
|
||||||
- email: ${ADMIN_EMAIL:-admin@example.com}
|
|
||||||
hash: ${ADMIN_PASSWORD_HASH}
|
|
||||||
username: ${ADMIN_USERNAME:-admin}
|
|
||||||
userID: "admin-001"
|
|
||||||
+3
-2
@@ -5,8 +5,9 @@ import "time"
|
|||||||
type (
|
type (
|
||||||
User struct {
|
User struct {
|
||||||
ID uint `json:"id" gorm:"primarykey"`
|
ID uint `json:"id" gorm:"primarykey"`
|
||||||
GitHubID int64 `json:"githubId" gorm:"unique"`
|
Subject string `json:"subject" gorm:"uniqueIndex"`
|
||||||
Login string `json:"login"`
|
Login string `json:"login" gorm:"uniqueIndex"`
|
||||||
|
Email string `json:"email"`
|
||||||
AvatarURL string `json:"avatarUrl"`
|
AvatarURL string `json:"avatarUrl"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
|||||||
+19
-9
@@ -8,20 +8,30 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5556:5556"
|
- "5556:5556"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/dex.config.yml:/etc/dex/config.yml
|
- ./config/dex.config.yaml:/etc/dex/config.yaml
|
||||||
- dex-data:/var/lib/dex
|
|
||||||
environment:
|
environment:
|
||||||
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
|
- OIDC_REDIRECT_URL=${OIDC_REDIRECT_URL:-http://localhost:3000/auth/oidc/callback}
|
||||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
|
|
||||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
|
||||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
|
||||||
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
|
|
||||||
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET:-excalidraw-secret}
|
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET:-excalidraw-secret}
|
||||||
|
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-excalidraw}
|
||||||
|
- OIDC_ISSUER=${OIDC_ISSUER:-http://localhost:5556}
|
||||||
|
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||||
|
- ADMIN_PASSWORD_HASH=${ADMIN_PASSWORD_HASH:-your_secure_password}
|
||||||
|
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
|
||||||
|
- ADMIN_USER_ID=${ADMIN_USER_ID:-'admin1234'}
|
||||||
|
command: ["dex", "serve", "/etc/dex/config.yaml"]
|
||||||
networks:
|
networks:
|
||||||
- dex-network
|
- dex-network
|
||||||
|
|
||||||
volumes:
|
excalidraw:
|
||||||
dex-data:
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: excalidraw-complete.Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3003:3002"
|
||||||
|
volumes:
|
||||||
|
- ./data:/root/data
|
||||||
|
- ./excalidraw.db:/root/excalidraw.db:Z
|
||||||
|
- ./.env:/root/.env
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
dex-network:
|
dex-network:
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
|
||||||
github.com/aws/smithy-go v1.20.1 // indirect
|
github.com/aws/smithy-go v1.20.1 // indirect
|
||||||
|
github.com/coreos/go-oidc/v3 v3.15.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3Fajf
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0=
|
||||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -51,6 +53,8 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
|||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"excalidraw-complete/core"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidcOauthConfig *oauth2.Config
|
||||||
|
oidcProvider *oidc.Provider
|
||||||
|
verifier *oidc.IDTokenVerifier
|
||||||
|
)
|
||||||
|
|
||||||
|
// OIDCClaims represents the claims from OIDC token
|
||||||
|
type OIDCClaims struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PreferredUsername string `json:"preferred_username"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDex() {
|
||||||
|
providerURL := os.Getenv("OIDC_ISSUER_URL")
|
||||||
|
clientID := os.Getenv("OIDC_CLIENT_ID")
|
||||||
|
clientSecret := os.Getenv("OIDC_CLIENT_SECRET")
|
||||||
|
redirectURL := os.Getenv("OIDC_REDIRECT_URL")
|
||||||
|
|
||||||
|
if providerURL == "" || clientID == "" || clientSecret == "" {
|
||||||
|
logrus.Warn("OIDC credentials are not set. OIDC authentication routes will not work.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
oidcProvider, err = oidc.NewProvider(context.Background(), providerURL)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to create OIDC provider: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcOauthConfig = &oauth2.Config{
|
||||||
|
ClientID: clientID,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
RedirectURL: redirectURL,
|
||||||
|
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||||
|
Endpoint: oidcProvider.Endpoint(),
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Info("OIDC provider initialized")
|
||||||
|
|
||||||
|
verifier = oidcProvider.Verifier(&oidc.Config{
|
||||||
|
ClientID: clientID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleOIDCLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if oidcOauthConfig == nil {
|
||||||
|
http.Error(w, "OIDC is not configured", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := oidcOauthConfig.AuthCodeURL("random", oauth2.AccessTypeOffline)
|
||||||
|
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if oidcOauthConfig == nil {
|
||||||
|
http.Error(w, "OIDC is not configured", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := r.FormValue("code")
|
||||||
|
if code == "" {
|
||||||
|
logrus.Error("no code in callback")
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := oidcOauthConfig.Exchange(context.Background(), code)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to exchange token: %s", err.Error())
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawIDToken, ok := token.Extra("id_token").(string)
|
||||||
|
if !ok {
|
||||||
|
logrus.Error("no id_token in token response")
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
idToken, err := verifier.Verify(context.Background(), rawIDToken)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to verify ID token: %s", err.Error())
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims OIDCClaims
|
||||||
|
if err := idToken.Claims(&claims); err != nil {
|
||||||
|
logrus.Errorf("failed to extract claims from ID token: %s", err.Error())
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create user from OIDC claims
|
||||||
|
user := &core.User{
|
||||||
|
Subject: claims.Sub,
|
||||||
|
Login: claims.PreferredUsername,
|
||||||
|
Email: claims.Email,
|
||||||
|
AvatarURL: claims.Picture,
|
||||||
|
Name: claims.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If preferred_username is not available, use email
|
||||||
|
if user.Login == "" && user.Email != "" {
|
||||||
|
user.Login = user.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
jwtToken, err := createOIDCJWT(user)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to create JWT: %s", err.Error())
|
||||||
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to frontend with token
|
||||||
|
http.Redirect(w, r, fmt.Sprintf("/?token=%s", jwtToken), http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createOIDCJWT(user *core.User) (string, error) {
|
||||||
|
claims := AppClaims{
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
Subject: user.Subject,
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 1 week
|
||||||
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
},
|
||||||
|
Login: user.Login,
|
||||||
|
AvatarURL: user.AvatarURL,
|
||||||
|
Name: user.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
return token.SignedString(jwtSecret)
|
||||||
|
}
|
||||||
@@ -23,7 +23,6 @@ var (
|
|||||||
jwtSecret []byte
|
jwtSecret []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
const oauthStateString = "random"
|
|
||||||
|
|
||||||
// AppClaims represents the custom claims for the JWT.
|
// AppClaims represents the custom claims for the JWT.
|
||||||
type AppClaims struct {
|
type AppClaims struct {
|
||||||
@@ -124,10 +123,9 @@ func HandleGitHubCallback(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now we don't have a user database, so we create a user object on the fly.
|
// Create user object using Subject instead of GitHubID
|
||||||
// In phase 3, we will save/get the user from the database here.
|
|
||||||
user := &core.User{
|
user := &core.User{
|
||||||
GitHubID: githubUser.ID,
|
Subject: fmt.Sprintf("github:%d", githubUser.ID),
|
||||||
Login: githubUser.Login,
|
Login: githubUser.Login,
|
||||||
AvatarURL: githubUser.AvatarURL,
|
AvatarURL: githubUser.AvatarURL,
|
||||||
Name: githubUser.Name,
|
Name: githubUser.Name,
|
||||||
@@ -147,7 +145,7 @@ func HandleGitHubCallback(w http.ResponseWriter, r *http.Request) {
|
|||||||
func createJWT(user *core.User) (string, error) {
|
func createJWT(user *core.User) (string, error) {
|
||||||
claims := AppClaims{
|
claims := AppClaims{
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
Subject: fmt.Sprintf("%d", user.GitHubID),
|
Subject: user.Subject,
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 1 week
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 1 week
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -171,6 +171,11 @@ func setupRouter(store stores.Store) *chi.Mux {
|
|||||||
r.Get("/callback", auth.HandleGitHubCallback)
|
r.Get("/callback", auth.HandleGitHubCallback)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Route("/auth/oidc", func(r chi.Router) {
|
||||||
|
r.Get("/login", auth.HandleOIDCLogin)
|
||||||
|
r.Get("/callback", auth.HandleOIDCCallback)
|
||||||
|
})
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +312,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
auth.Init()
|
auth.Init()
|
||||||
|
auth.InitDex()
|
||||||
openai.Init()
|
openai.Init()
|
||||||
store := stores.GetStore()
|
store := stores.GetStore()
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# 检查环境变量
|
|
||||||
if [ -z "$ADMIN_PASSWORD" ]; then
|
|
||||||
echo "错误: 请设置 ADMIN_PASSWORD 环境变量"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 生成密码哈希
|
|
||||||
echo "正在生成密码哈希..."
|
|
||||||
PASSWORD_HASH=$(docker run --rm dexidp/dex:v2.38.0 hash --password="$ADMIN_PASSWORD")
|
|
||||||
|
|
||||||
# 创建临时配置文件
|
|
||||||
cat > /tmp/dex-init-config.yml << EOF
|
|
||||||
issuer: http://localhost:5556
|
|
||||||
|
|
||||||
storage:
|
|
||||||
type: sqlite3
|
|
||||||
config:
|
|
||||||
file: /var/lib/dex/dex.db
|
|
||||||
|
|
||||||
web:
|
|
||||||
http: 0.0.0.0:5556
|
|
||||||
|
|
||||||
logger:
|
|
||||||
level: info
|
|
||||||
|
|
||||||
enablePasswordDB: true
|
|
||||||
|
|
||||||
staticPasswords:
|
|
||||||
- email: ${ADMIN_EMAIL:-admin@example.com}
|
|
||||||
hash: $PASSWORD_HASH
|
|
||||||
username: ${ADMIN_USERNAME:-admin}
|
|
||||||
userID: "admin-001"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# 初始化 Dex 数据库
|
|
||||||
echo "正在初始化 Dex 数据库..."
|
|
||||||
docker run --rm \
|
|
||||||
-v $(pwd)/config/dex.config.yml:/etc/dex/config.yml \
|
|
||||||
-v dex-data:/var/lib/dex \
|
|
||||||
dexidp/dex:v2.38.0 \
|
|
||||||
serve /etc/dex/config.yml &
|
|
||||||
DEX_PID=$!
|
|
||||||
|
|
||||||
# 等待 Dex 启动
|
|
||||||
echo "等待 Dex 启动..."
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# 停止临时 Dex 进程
|
|
||||||
kill $DEX_PID 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "Dex 用户初始化完成!"
|
|
||||||
echo "管理员账户:"
|
|
||||||
echo " 用户名: ${ADMIN_USERNAME:-admin}"
|
|
||||||
echo " 邮箱: ${ADMIN_EMAIL:-admin@example.com}"
|
|
||||||
echo " 密码: $ADMIN_PASSWORD"
|
|
||||||
echo ""
|
|
||||||
echo "请使用以下凭据登录:"
|
|
||||||
echo " Dex UI: http://localhost:5556"
|
|
||||||
echo " 用户名: ${ADMIN_USERNAME:-admin}"
|
|
||||||
echo " 密码: $ADMIN_PASSWORD"
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 生成随机密码
|
|
||||||
generate_password() {
|
|
||||||
openssl rand -base64 16 | tr -d "=+/" | cut -c1-16
|
|
||||||
}
|
|
||||||
|
|
||||||
# 生成 JWT 密钥
|
|
||||||
generate_jwt_secret() {
|
|
||||||
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "正在生成环境变量配置..."
|
|
||||||
|
|
||||||
# 生成随机密码和密钥
|
|
||||||
ADMIN_PASSWORD=$(generate_password)
|
|
||||||
JWT_SECRET=$(generate_jwt_secret)
|
|
||||||
OIDC_CLIENT_SECRET=$(generate_password)
|
|
||||||
|
|
||||||
# 创建 .env 文件
|
|
||||||
cat > .env << EOF
|
|
||||||
# === 认证配置 ===
|
|
||||||
AUTH_TYPE=oidc
|
|
||||||
|
|
||||||
# GitHub OAuth 配置 (可选)
|
|
||||||
# GITHUB_CLIENT_ID=your_github_client_id
|
|
||||||
# GITHUB_CLIENT_SECRET=your_github_client_secret
|
|
||||||
# GITHUB_REDIRECT_URL=http://localhost:3002/auth/github/callback
|
|
||||||
|
|
||||||
# OIDC 配置
|
|
||||||
OIDC_ISSUER_URL=http://localhost:5556/.well-known/openid-configuration
|
|
||||||
OIDC_CLIENT_ID=excalidraw
|
|
||||||
OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
|
||||||
OIDC_REDIRECT_URL=http://localhost:3002/auth/oidc/callback
|
|
||||||
|
|
||||||
# Dex 配置
|
|
||||||
OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
|
||||||
ADMIN_USERNAME=admin
|
|
||||||
ADMIN_PASSWORD=$ADMIN_PASSWORD
|
|
||||||
ADMIN_EMAIL=admin@example.com
|
|
||||||
|
|
||||||
# === JWT 配置 ===
|
|
||||||
JWT_SECRET=$JWT_SECRET
|
|
||||||
|
|
||||||
# === 存储配置 ===
|
|
||||||
STORAGE_TYPE=sqlite
|
|
||||||
DATA_SOURCE_NAME=excalidraw.db
|
|
||||||
LOCAL_STORAGE_PATH=./data
|
|
||||||
|
|
||||||
# === 应用配置 ===
|
|
||||||
LISTEN=:3002
|
|
||||||
LOG_LEVEL=info
|
|
||||||
|
|
||||||
# === OpenAI 配置 (可选) ===
|
|
||||||
# OPENAI_API_KEY=sk-your_openai_api_key
|
|
||||||
# OPENAI_BASE_URL=https://api.openai.com
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "环境变量配置已生成到 .env 文件"
|
|
||||||
echo ""
|
|
||||||
echo "重要信息请保存:"
|
|
||||||
echo " 管理员密码: $ADMIN_PASSWORD"
|
|
||||||
echo " JWT 密钥: $JWT_SECRET"
|
|
||||||
echo " Dex 客户端密钥: $OIDC_CLIENT_SECRET"
|
|
||||||
echo ""
|
|
||||||
echo "请运行以下命令启动服务:"
|
|
||||||
echo " 1. docker-compose -f docker-compose.dex.yml up -d"
|
|
||||||
echo " 2. ./scripts/init-dex-users.sh"
|
|
||||||
echo " 3. docker-compose up -d"
|
|
||||||
Reference in New Issue
Block a user