11package authservice
22
33import (
4+ "errors"
5+ "fmt"
46 "net/http"
57 "net/url"
68
@@ -16,7 +18,10 @@ import (
1618 "google.golang.org/api/option"
1719)
1820
19- const verifierCookieName = "Gauth-Verifier"
21+ const (
22+ verifierCookieName = "Gauth-Verifier"
23+ redirectCookieName = "Gauth-Redirect"
24+ )
2025
2126// BuildOAuthConfig builds an oauth2.Config from a gauthConfig.
2227func BuildOAuthConfig (gauthConfig config.GAuthConfig ) * oauth2.Config {
@@ -32,13 +37,13 @@ func BuildOAuthConfig(gauthConfig config.GAuthConfig) *oauth2.Config {
3237}
3338
3439type GauthHandler struct {
35- oauthConfig * oauth2.Config
36- useraccount * useraccount.Context
37- redirectURL string
40+ oauthConfig * oauth2.Config
41+ useraccount * useraccount.Context
42+ redirectURIs [] string
3843}
3944
40- func NewGauthHandler (oauthConfig * oauth2.Config , useraccount * useraccount.Context , redirectURL string ) * GauthHandler {
41- return & GauthHandler {oauthConfig : oauthConfig , useraccount : useraccount , redirectURL : redirectURL }
45+ func NewGauthHandler (oauthConfig * oauth2.Config , useraccount * useraccount.Context , redirectURIs [] string ) * GauthHandler {
46+ return & GauthHandler {oauthConfig : oauthConfig , useraccount : useraccount , redirectURIs : redirectURIs }
4247}
4348
4449func (h * GauthHandler ) Login (c * gin.Context ) {
@@ -55,6 +60,11 @@ func (h *GauthHandler) Login(c *gin.Context) {
5560 return
5661 }
5762
63+ redirectURI := c .Query ("redirect_uri" )
64+ if redirectURI == "" {
65+ redirectURI = h .oauthConfig .RedirectURL
66+ }
67+
5868 callbackURL , err := url .Parse (h .oauthConfig .RedirectURL )
5969 if err != nil {
6070 c .JSON (http .StatusInternalServerError , gin.H {
@@ -74,6 +84,16 @@ func (h *GauthHandler) Login(c *gin.Context) {
7484 /* httpOnly */ true ,
7585 )
7686
87+ c .SetCookie (
88+ /* name */ redirectCookieName ,
89+ /* value */ redirectURI ,
90+ /* maxAge */ 5 * 60 , // 5 min
91+ /* path */ "/" ,
92+ /* domain */ "" ,
93+ /* secure */ true ,
94+ /* httpOnly */ true ,
95+ )
96+
7797 redirectURL := h .oauthConfig .AuthCodeURL (
7898 "" ,
7999 oauth2 .AccessTypeOnline ,
@@ -160,5 +180,55 @@ func (h *GauthHandler) Callback(c *gin.Context) {
160180 /* httpOnly */ true ,
161181 )
162182
163- c .Redirect (http .StatusTemporaryRedirect , h .redirectURL )
183+ // redirect to the original redirect URL
184+ redirectURL , err := c .Cookie (redirectCookieName )
185+ if err != nil {
186+ if errors .Is (err , http .ErrNoCookie ) {
187+ c .JSON (http .StatusOK , gin.H {
188+ "success" : true ,
189+ })
190+ return
191+ }
192+
193+ c .JSON (http .StatusInternalServerError , gin.H {
194+ "error" : "failed to get redirect URL" ,
195+ "detail" : err .Error (),
196+ })
197+ return
198+ }
199+
200+ // check if the redirect URL is in the allowed redirect URIs
201+ userRedirectURL , err := url .Parse (redirectURL )
202+ if err != nil {
203+ c .JSON (http .StatusInternalServerError , gin.H {
204+ "error" : "failed to parse redirect URL" ,
205+ "detail" : err .Error (),
206+ })
207+ return
208+ }
209+
210+ for _ , allowedRedirectURI := range h .redirectURIs {
211+ parsedAllowedRedirectURI , err := url .Parse (allowedRedirectURI )
212+ if err != nil {
213+ c .JSON (http .StatusInternalServerError , gin.H {
214+ "error" : "failed to parse allowed redirect URI" ,
215+ "detail" : err .Error (),
216+ })
217+ return
218+ }
219+
220+ matched := userRedirectURL .Scheme == parsedAllowedRedirectURI .Scheme &&
221+ userRedirectURL .Host == parsedAllowedRedirectURI .Host &&
222+ userRedirectURL .Path == parsedAllowedRedirectURI .Path
223+
224+ if matched {
225+ c .Redirect (http .StatusTemporaryRedirect , parsedAllowedRedirectURI .String ())
226+ return
227+ }
228+ }
229+
230+ c .JSON (http .StatusBadRequest , gin.H {
231+ "error" : "redirect URL is not allowed" ,
232+ "detail" : fmt .Sprintf ("redirect URL is not allowed: %s" , redirectURL ),
233+ })
164234}
0 commit comments