Skip to content

Commit 881d6e2

Browse files
authored
feat: add OIDC single sign-on (AlistGo#4496)
close AlistGo#3914 close AlistGo#4315
1 parent bd2418c commit 881d6e2

File tree

4 files changed

+135
-3
lines changed

4 files changed

+135
-3
lines changed

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/aws/aws-sdk-go v1.44.262
1010
github.com/blevesearch/bleve/v2 v2.3.8
1111
github.com/caarlos0/env/v7 v7.1.0
12+
github.com/coreos/go-oidc v2.2.1+incompatible
1213
github.com/deckarep/golang-set/v2 v2.3.0
1314
github.com/disintegration/imaging v1.6.2
1415
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
@@ -36,6 +37,7 @@ require (
3637
golang.org/x/crypto v0.9.0
3738
golang.org/x/image v0.7.0
3839
golang.org/x/net v0.10.0
40+
golang.org/x/oauth2 v0.4.0
3941
gorm.io/driver/mysql v1.4.7
4042
gorm.io/driver/postgres v1.4.8
4143
gorm.io/driver/sqlite v1.4.4
@@ -118,6 +120,7 @@ require (
118120
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 // indirect
119121
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
120122
github.com/pierrec/lz4/v4 v4.1.17 // indirect
123+
github.com/pquerna/cachecontrol v0.1.0 // indirect
121124
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
122125
github.com/spaolacci/murmur3 v1.1.0 // indirect
123126
github.com/spf13/pflag v1.0.5 // indirect
@@ -130,8 +133,10 @@ require (
130133
golang.org/x/sys v0.8.0 // indirect
131134
golang.org/x/text v0.9.0 // indirect
132135
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
136+
google.golang.org/appengine v1.6.7 // indirect
133137
google.golang.org/protobuf v1.28.1 // indirect
134138
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
139+
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
135140
gopkg.in/yaml.v3 v3.0.1 // indirect
136141
lukechampine.com/blake3 v1.1.7 // indirect
137142
)

go.sum

+13-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyV
6666
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
6767
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
6868
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
69+
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
70+
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
6971
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
7072
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg=
7173
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
@@ -119,13 +121,14 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW
119121
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
120122
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
121123
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
124+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
122125
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
123126
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
124127
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
125128
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
126129
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
127-
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
128130
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
131+
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
129132
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
130133
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
131134
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -249,6 +252,8 @@ github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
249252
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
250253
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
251254
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
255+
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
256+
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
252257
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
253258
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
254259
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -324,6 +329,7 @@ golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
324329
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
325330
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
326331
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
332+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
327333
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
328334
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
329335
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -334,6 +340,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
334340
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
335341
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
336342
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
343+
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
344+
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
337345
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
338346
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
339347
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -384,7 +392,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
384392
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
385393
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
386394
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
387-
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
395+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
396+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
388397
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
389398
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
390399
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
@@ -397,6 +406,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
397406
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
398407
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
399408
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
409+
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
410+
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
400411
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
401412
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
402413
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

internal/bootstrap/data/setting.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func InitialSettings() []model.SettingItem {
154154

155155
// SSO settings
156156
{Key: conf.SSOLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.SSO, Flag: model.PUBLIC},
157-
{Key: conf.SSOLoginplatform, Type: conf.TypeSelect, Options: "Casdoor,Github,Microsoft,Google,Dingtalk", Group: model.SSO, Flag: model.PUBLIC},
157+
{Key: conf.SSOLoginplatform, Type: conf.TypeSelect, Options: "Casdoor,Github,Microsoft,Google,Dingtalk,OIDC", Group: model.SSO, Flag: model.PUBLIC},
158158
{Key: conf.SSOClientId, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
159159
{Key: conf.SSOClientSecret, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
160160
{Key: conf.SSOOrganizationName, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},

server/handles/ssologin.go

+116
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package handles
33
import (
44
"errors"
55
"fmt"
6+
"net/http"
67
"net/url"
78
"strings"
89

@@ -11,8 +12,10 @@ import (
1112
"github.com/alist-org/alist/v3/internal/setting"
1213
"github.com/alist-org/alist/v3/pkg/utils"
1314
"github.com/alist-org/alist/v3/server/common"
15+
"github.com/coreos/go-oidc"
1416
"github.com/gin-gonic/gin"
1517
"github.com/go-resty/resty/v2"
18+
"golang.org/x/oauth2"
1619
)
1720

1821
func SSOLoginRedirect(c *gin.Context) {
@@ -53,6 +56,14 @@ func SSOLoginRedirect(c *gin.Context) {
5356
r_url = endpoint + "/login/oauth/authorize?"
5457
urlValues.Add("scope", "profile")
5558
urlValues.Add("state", endpoint)
59+
case "OIDC":
60+
oauth2Config, err := GetOIDCClient(c)
61+
if err != nil {
62+
common.ErrorStrResp(c, err.Error(), 400)
63+
return
64+
}
65+
c.Redirect(http.StatusFound, oauth2Config.AuthCodeURL("state"))
66+
return
5667
default:
5768
common.ErrorStrResp(c, "invalid platform", 400)
5869
return
@@ -65,6 +76,108 @@ func SSOLoginRedirect(c *gin.Context) {
6576

6677
var ssoClient = resty.New().SetRetryCount(3)
6778

79+
func GetOIDCClient(c *gin.Context) (*oauth2.Config, error) {
80+
argument := c.Query("method")
81+
redirect_uri := common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + argument
82+
endpoint := setting.GetStr(conf.SSOEndpointName)
83+
provider, err := oidc.NewProvider(c, endpoint)
84+
if err != nil {
85+
return nil, err
86+
}
87+
clientId := setting.GetStr(conf.SSOClientId)
88+
clientSecret := setting.GetStr(conf.SSOClientSecret)
89+
return &oauth2.Config{
90+
ClientID: clientId,
91+
ClientSecret: clientSecret,
92+
RedirectURL: redirect_uri,
93+
94+
// Discovery returns the OAuth2 endpoints.
95+
Endpoint: provider.Endpoint(),
96+
97+
// "openid" is a required scope for OpenID Connect flows.
98+
Scopes: []string{oidc.ScopeOpenID, "profile"},
99+
}, nil
100+
}
101+
102+
func OIDCLoginCallback(c *gin.Context) {
103+
argument := c.Query("method")
104+
enabled := setting.GetBool(conf.SSOLoginEnabled)
105+
clientId := setting.GetStr(conf.SSOClientId)
106+
if !enabled {
107+
common.ErrorResp(c, errors.New("invalid request"), 500)
108+
}
109+
endpoint := setting.GetStr(conf.SSOEndpointName)
110+
provider, err := oidc.NewProvider(c, endpoint)
111+
if err != nil {
112+
common.ErrorResp(c, err, 400)
113+
return
114+
}
115+
oauth2Config, err := GetOIDCClient(c)
116+
if err != nil {
117+
common.ErrorResp(c, err, 400)
118+
return
119+
}
120+
oauth2Token, err := oauth2Config.Exchange(c, c.Query("code"))
121+
if err != nil {
122+
common.ErrorResp(c, err, 400)
123+
return
124+
}
125+
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
126+
if !ok {
127+
common.ErrorStrResp(c, "no id_token found in oauth2 token", 400)
128+
return
129+
}
130+
verifier := provider.Verifier(&oidc.Config{
131+
ClientID: clientId,
132+
})
133+
idToken, err := verifier.Verify(c, rawIDToken)
134+
if err != nil {
135+
common.ErrorResp(c, err, 400)
136+
return
137+
}
138+
type UserInfo struct {
139+
Name string `json:"name"`
140+
}
141+
claims := UserInfo{}
142+
if err := idToken.Claims(&claims); err != nil {
143+
c.Error(err)
144+
return
145+
}
146+
UserID := claims.Name
147+
if argument == "get_sso_id" {
148+
html := fmt.Sprintf(`<!DOCTYPE html>
149+
<head></head>
150+
<body>
151+
<script>
152+
window.opener.postMessage({"sso_id": "%s"}, "*")
153+
window.close()
154+
</script>
155+
</body>`, UserID)
156+
c.Data(200, "text/html; charset=utf-8", []byte(html))
157+
return
158+
}
159+
if argument == "sso_get_token" {
160+
user, err := db.GetUserBySSOID(UserID)
161+
if err != nil {
162+
common.ErrorResp(c, err, 400)
163+
}
164+
token, err := common.GenerateToken(user.Username)
165+
if err != nil {
166+
common.ErrorResp(c, err, 400)
167+
}
168+
html := fmt.Sprintf(`<!DOCTYPE html>
169+
<head></head>
170+
<body>
171+
<script>
172+
window.opener.postMessage({"token":"%s"}, "*")
173+
window.close()
174+
</script>
175+
</body>`, token)
176+
c.Data(200, "text/html; charset=utf-8", []byte(html))
177+
return
178+
}
179+
}
180+
68181
func SSOLoginCallback(c *gin.Context) {
69182
argument := c.Query("method")
70183
if argument == "get_sso_id" || argument == "sso_get_token" {
@@ -108,6 +221,9 @@ func SSOLoginCallback(c *gin.Context) {
108221
scope = "profile"
109222
authstring = "code"
110223
idstring = "preferred_username"
224+
case "OIDC":
225+
OIDCLoginCallback(c)
226+
return
111227
default:
112228
common.ErrorStrResp(c, "invalid platform", 400)
113229
return

0 commit comments

Comments
 (0)