@@ -3,6 +3,7 @@ package handles
3
3
import (
4
4
"errors"
5
5
"fmt"
6
+ "net/http"
6
7
"net/url"
7
8
"strings"
8
9
@@ -11,8 +12,10 @@ import (
11
12
"github.com/alist-org/alist/v3/internal/setting"
12
13
"github.com/alist-org/alist/v3/pkg/utils"
13
14
"github.com/alist-org/alist/v3/server/common"
15
+ "github.com/coreos/go-oidc"
14
16
"github.com/gin-gonic/gin"
15
17
"github.com/go-resty/resty/v2"
18
+ "golang.org/x/oauth2"
16
19
)
17
20
18
21
func SSOLoginRedirect (c * gin.Context ) {
@@ -53,6 +56,14 @@ func SSOLoginRedirect(c *gin.Context) {
53
56
r_url = endpoint + "/login/oauth/authorize?"
54
57
urlValues .Add ("scope" , "profile" )
55
58
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
56
67
default :
57
68
common .ErrorStrResp (c , "invalid platform" , 400 )
58
69
return
@@ -65,6 +76,108 @@ func SSOLoginRedirect(c *gin.Context) {
65
76
66
77
var ssoClient = resty .New ().SetRetryCount (3 )
67
78
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
+
68
181
func SSOLoginCallback (c * gin.Context ) {
69
182
argument := c .Query ("method" )
70
183
if argument == "get_sso_id" || argument == "sso_get_token" {
@@ -108,6 +221,9 @@ func SSOLoginCallback(c *gin.Context) {
108
221
scope = "profile"
109
222
authstring = "code"
110
223
idstring = "preferred_username"
224
+ case "OIDC" :
225
+ OIDCLoginCallback (c )
226
+ return
111
227
default :
112
228
common .ErrorStrResp (c , "invalid platform" , 400 )
113
229
return
0 commit comments