From 2837563147d18dba263ed4c7a1b87571474220c4 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Thu, 12 Sep 2019 22:15:36 -0400 Subject: [PATCH] oauth2 with remote Gitea - Fix #8093 (#8149) --- go.mod | 2 +- go.sum | 4 +- models/oauth2.go | 8 + modules/auth/oauth2/oauth2.go | 23 +++ options/locale/locale_en-US.ini | 1 + public/img/auth/gitea.png | Bin 0 -> 5576 bytes public/js/index.js | 2 + templates/admin/auth/new.tmpl | 2 + vendor/github.com/markbates/goth/.travis.yml | 1 + vendor/github.com/markbates/goth/README.md | 5 + .../markbates/goth/gothic/gothic.go | 24 ++- .../markbates/goth/providers/gitea/gitea.go | 186 ++++++++++++++++++ .../markbates/goth/providers/gitea/session.go | 63 ++++++ vendor/modules.txt | 3 +- 14 files changed, 307 insertions(+), 17 deletions(-) create mode 100644 public/img/auth/gitea.png create mode 100644 vendor/github.com/markbates/goth/providers/gitea/gitea.go create mode 100644 vendor/github.com/markbates/goth/providers/gitea/session.go diff --git a/go.mod b/go.mod index d72269abdd..41e2297934 100644 --- a/go.mod +++ b/go.mod @@ -68,8 +68,8 @@ require ( github.com/lib/pq v1.2.0 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e + github.com/markbates/goth v1.56.0 github.com/mailru/easyjson v0.7.0 // indirect - github.com/markbates/goth v1.49.0 github.com/mattn/go-isatty v0.0.7 github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect github.com/mattn/go-sqlite3 v1.11.0 diff --git a/go.sum b/go.sum index 425a83c314..a0eff427da 100644 --- a/go.sum +++ b/go.sum @@ -408,8 +408,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA= -github.com/markbates/goth v1.49.0 h1:qQ4Ti4WaqAxNAggOC+4s5M85sMVfMJwQn/Xkp73wfgI= -github.com/markbates/goth v1.49.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA= +github.com/markbates/goth v1.56.0 h1:XEYedCgMNz5pi3ojXI8z2XUmXtBnMeuKUpx4Z6HlNj8= +github.com/markbates/goth v1.56.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g= diff --git a/models/oauth2.go b/models/oauth2.go index bf4446229a..c82c1cbadb 100644 --- a/models/oauth2.go +++ b/models/oauth2.go @@ -44,6 +44,13 @@ var OAuth2Providers = map[string]OAuth2Provider{ "openidConnect": {Name: "openidConnect", DisplayName: "OpenID Connect", Image: "/img/auth/openid_connect.png"}, "twitter": {Name: "twitter", DisplayName: "Twitter", Image: "/img/auth/twitter.png"}, "discord": {Name: "discord", DisplayName: "Discord", Image: "/img/auth/discord.png"}, + "gitea": {Name: "gitea", DisplayName: "Gitea", Image: "/img/auth/gitea.png", + CustomURLMapping: &oauth2.CustomURLMapping{ + TokenURL: oauth2.GetDefaultTokenURL("gitea"), + AuthURL: oauth2.GetDefaultAuthURL("gitea"), + ProfileURL: oauth2.GetDefaultProfileURL("gitea"), + }, + }, } // OAuth2DefaultCustomURLMappings contains the map of default URL's for OAuth2 providers that are allowed to have custom urls @@ -52,6 +59,7 @@ var OAuth2Providers = map[string]OAuth2Provider{ var OAuth2DefaultCustomURLMappings = map[string]*oauth2.CustomURLMapping{ "github": OAuth2Providers["github"].CustomURLMapping, "gitlab": OAuth2Providers["gitlab"].CustomURLMapping, + "gitea": OAuth2Providers["gitea"].CustomURLMapping, } // GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources diff --git a/modules/auth/oauth2/oauth2.go b/modules/auth/oauth2/oauth2.go index a2d7116211..de2efd0463 100644 --- a/modules/auth/oauth2/oauth2.go +++ b/modules/auth/oauth2/oauth2.go @@ -19,6 +19,7 @@ import ( "github.com/markbates/goth/providers/discord" "github.com/markbates/goth/providers/dropbox" "github.com/markbates/goth/providers/facebook" + "github.com/markbates/goth/providers/gitea" "github.com/markbates/goth/providers/github" "github.com/markbates/goth/providers/gitlab" "github.com/markbates/goth/providers/gplus" @@ -175,6 +176,22 @@ func createProvider(providerName, providerType, clientID, clientSecret, openIDCo provider = twitter.NewAuthenticate(clientID, clientSecret, callbackURL) case "discord": provider = discord.New(clientID, clientSecret, callbackURL, discord.ScopeIdentify, discord.ScopeEmail) + case "gitea": + authURL := gitea.AuthURL + tokenURL := gitea.TokenURL + profileURL := gitea.ProfileURL + if customURLMapping != nil { + if len(customURLMapping.AuthURL) > 0 { + authURL = customURLMapping.AuthURL + } + if len(customURLMapping.TokenURL) > 0 { + tokenURL = customURLMapping.TokenURL + } + if len(customURLMapping.ProfileURL) > 0 { + profileURL = customURLMapping.ProfileURL + } + } + provider = gitea.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL) } // always set the name if provider is created so we can support multiple setups of 1 provider @@ -192,6 +209,8 @@ func GetDefaultTokenURL(provider string) string { return github.TokenURL case "gitlab": return gitlab.TokenURL + case "gitea": + return gitea.TokenURL } return "" } @@ -203,6 +222,8 @@ func GetDefaultAuthURL(provider string) string { return github.AuthURL case "gitlab": return gitlab.AuthURL + case "gitea": + return gitea.AuthURL } return "" } @@ -214,6 +235,8 @@ func GetDefaultProfileURL(provider string) string { return github.ProfileURL case "gitlab": return gitlab.ProfileURL + case "gitea": + return gitea.ProfileURL } return "" } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 36fabb5b4a..45e71e0595 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1706,6 +1706,7 @@ auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API con auths.tip.openid_connect = Use the OpenID Connect Discovery URL (/.well-known/openid-configuration) to specify the endpoints auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled auths.tip.discord = Register a new application on https://discordapp.com/developers/applications/me +auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.io/en-us/oauth2-provider/ auths.edit = Edit Authentication Source auths.activated = This Authentication Source is Activated auths.new_success = The authentication '%s' has been added. diff --git a/public/img/auth/gitea.png b/public/img/auth/gitea.png new file mode 100644 index 0000000000000000000000000000000000000000..30d331359463fb0d5e21f0faf9ea14a5f41e12e2 GIT binary patch literal 5576 zcmZ{cbx;$I!-qFYU`$j{8l*dE_jdV(nmKt5s-Q8W%qfuI|U&003kRiyHuV8?7KCsqr0n*y9fX z008jp#}c9vwEy(~$9#|+Zrjw$q_@! zPT2Fh?Zri0Dkto^>)Ee@V$+`6eu}?VPy3+xoMq`PDzM2$^C|L3yc((lKlt_Ke2H7L zGy2hf{`D<(GsU1h8+~O%g6N!6zhfi!V#W~1=cF>KbtWGax8~B?WX;MSfhsOd+&cXS z_5QK8Dja^}l@U9>YsI-wNkzQ5ZY%X#vTW~1BB-aEn02suidD~7Jq_u8%3Wr=YTz$Q z3=*#iE1cL#CLVqw=21W=^R2ILeBbqBq>4D57d-v#QM?71yk;Sv{OjxrGE z0Sk*!#*xOc<=3+HBk|AET3gI(qx0VJWxvD)cNkAeK!IBk2v*7<=Hncp!yg|aFYFu= znj>ZV@4^-svBMW4zm1Hv>%VLet-)Wjlrcu)jq&hk>eVdc{(gcTw4d&Qyv8D*@1k_i znkl9l(;q2$X-=Z&Uw%!~?s@$)=>v5m4>9c+GQ~e_AHBaQa?ob(yU@(wgu^&Wi$m4h zk`vJ>FEr`UU81($MXEBeA7lR~qs1PLy#xr|#H!|)gj5#o{}IS3+7|?1F=|V*cR%Jf zKfmBrmkdJEycg>D*juz~ibfIc9(6!ypuh#szn&t8avSxENO&EG<yQ-%r*@mXOzckQ*3DDTCZnW#8P;A;Y45Y%zHvqE)>+ zHnh)_?IH6a=%13Ag)-eg&{}!*O`WI>F@4Xq7bVjg0_;wt(4&LV>)8lL^-}dGpVTcd zS4`T?VRs+5!-DyF0$6P=naJce%jBO7I1?Np!O$WU$8Wk2B**TV#kfx4oj5kcI0zwJ zDQdsi51s3VROR+)Efr&9661R66qCJHkRK*0{ERFS2P*oi15uZ-IWMP=OqP>kg)jJB z%nfQ*tY>JpXT=T89`v?GcO{rwt>|O1F~_1^eCO5NXSlgVMs5lQ2x@wU9ZX+hLLTt~ zo)<)3a496&f_Ca`?_EDr7?6#b=$_0s+Pd&@4m8>>HQLsZ*gqTYyx&1WHD%0Q&0YIj z4VS&}g-Z*+SzCy?{7{QVb-$+5fTas>cVHHC7H_)wAJhY48T- z?65dJ1)iToB|eo1eYd{O;Z=P5!!*nFCs&wjdoi1VE&3uBZv=fJ($iSD71LRmaf6nF zH4$*%;Gu?p<>a9)4akd#0oZZk534hkPtvDX#>96$SBpmR6&)F)FJje?C=Ymw`1;>P zzRzE_K9^%!by`$YNRT+@P+3qJ9wbw+ul&b%)IWoDISjA-ai;ut@C?NK4Zmgi^nfZB zghqk0PMyy#Jo_H#licB5`m@!#z(#@=OS*V=?ksTGbRF$Rd<8_x$f_t(Dnc{xEPiAk z9H;c04wsX%L>I1xHwy96d|!JmtHcl|=Zt+VoX%nR@kNM2(s@kZzxQ$Jj0P(*UKwGURE-Fz;Uqg#UfV}}0oUb`IEP?`!z z!8(UyIseL--c1BZ+r$>D!V*75!Tp(UuvRQvXttWm8QL?GomaiF9&$}XVOM}rKL&z`6~c42N?o=ztpBy6^dbK3oJ@wKy<}#pl1F*BRIK`fL%=#stBq@v#3>jy*dHV8%izbv z={ok=3*TP7B-`ML$9=`}%3zgD<>^7Xf>Yy&v6J1)W}+1?cf@>ov=5jM)=ETVXZ7NK zX|=-Mr8=$tN0nY;HBfrwoQQ)pVY?<&LuKV$Hl+6UuP?|(`!X2?#CiO+x-92v+MS(J<*DvN&cAjFVLt9Hq_FJm{-c_kyCKSyA!bZLP*ps*OIVqlq@6AJtGFTO*(oR-GrPb3hT;=g|${In0$Rnna zZZxH>;-W+x2XdbZuc;F9*58=cs2p}&c$8OC*_RE2Px$qb0{0+lFm0y^ZZw`6LYMxd zYZimK7lyAC(avTq3PO@3U$xYrm1?v<0?hOghI-0pfYVTwHrR(>#(1iy&VGy@)n>~< z%bN}Aid2x6%Wuq1J$OgjpxmE1Nv?~zftM?39hJf@6M+?Ptao~p_r2#mjA+@gpY7^! zE$N2OTbgi`iCs*jaUKA`T?|n3S>zY&jujJJ40BhG~VcTBo*?+^LJZC$=~~DSPf#~&CI(>pUS!~K2@OR+2bC7v%3!Bm})0(O;Q$WfcE7^vF?@t|Dp(18oUpn z1%WuDo~3i1de;p;I*~pKw>>>3UK-B}Z}-pKR!3&{H09Cb=?G%pq&OuD7JbE2IV=Tl3rXjf?!j^?FV4ZE9K&A=^c7&Gs82inlII1}>tm9;?kVm$yml-z;jVORNeV*I$SxSSP$*(%S=E%PBqk?1A)N=2MB zRrvBr>;w@Rc+O2oYxC`(?%NzU9_Pqk1b2W&Y~c13q{B*;_m45`dcxcg6`$PAq#R?Fos@~_U@!h493)z`{7~lsM1BZ|QY%p)0#=LDD z_~!6a(TG#ECqQY#;7TL(M3%`9Hv2DqD<21AO2gC}%O2q?!9e_aHq9@y{a;t0g2#)yrQIeh>mV6vd*%#b{q=4RH z@E;qz3tQa+gKsZjj4Q8VhfH&j*4|KbvvP@yZ>^}`eC(Z9p<=V{$y@Y!)OSThG1>tP zf;{dS4Q_&;#atPZY%+7jC>`z>x+pvim^;D_5>>KKBZAnjoP>%6tJ7W3afAPIbV+$( z$f7L|RSVk~l^dkYap$_+cOJoeNn zIuDK_NgmtJ(McWiRFBOm3(Ndq;=Y$2<5@b`1zRRIV6BgLG4n6yaco+lr=wF79@p#V76GZ>LOiZh zyNuJhgLOA?Y> zZRB&Cxf1I8^aSgDo;_6#X4`x!8Kba+Qd8=4qqzZv0y&1*_O_~XzX`psv;4M$fH<|+ zwbm!^i)XZEiVk7@HQitbZQcz#W7Cj)CiFfoSw=rueB*#56T|C6K8qD5Gi=49zwy2s z7-j*rucPIS)od!fMWUU01D4zcQ+R>CttIKpBxs7Q&bA(S;XB{`c|RFwPSx8C{_|i@ z09q!}%w4J46=IT1y6!V(q8bkVnHPZW-mG|w)w|k*gfp;r=V@Un679bfhO<2c9ZQB0n+2aTX1w+Td!ncu;rxAP>IkBw;d=^y#t@lCp$xD-=I;=0&rD3yh z#!yu|uNoeQi+%G3&oQPi!tf5QS`U_@qQS5Kw(M>PwwzF=^e0zeNX9O1u`gc^AR=}T zRWg`zi*I`UhB=j>`9aKeANJEm(g6Aa3 zJF(NJ>z?Vd6#lq8j3E)HWRxtK(ns6Jyqe}H7kDQdu~&v=#XGZ~Gl6_FA-0j^rrEm7 zdmH%i0hXg1tJaLxfb5wVeZJ31Ee}2o>MqptaW~(Zk3>v<#QAIG()E`oUQT~WrSW-u z7?QbngD&^QG9qS~K+N=bp<#qQtn0{>g&2))x*IiR^bx+ntSlV&njtbmK$(9#ZKBbW zbkat8yW#S%$L5{BOV%}sI2p%`4$PA;2zF@jQh;Yh?3Kc(HN}mKVUJr zfzk4SA*d(sNr+A-(S-Zco)F5jV{l7H*t*cnHS4E^P`1ad5lc&O zR|8DkBAAO3bdHw4Vv?D|K9?{RJ-#_K>egFrwFzJiU_m@`9_a|iOqawNzN2E3O)of~ z6Zw!CTL3f6=h)k$V*B?^w*Z%yDxZaYpuO0aJt73Y!6*NjC8f<5X=S$eeAbSy7X5RP zr63R(XCF$1tbB#DWoq@nKDfSIfTDUn&+QtzXL+*EO|#S2ND_!CvPP9m&v~O%=%KGmQWhOIk3;G46jH-NVZ@MERPwq%SUp*WV2)J*~OO!Tj?wjUJ!3rzJe4>_prT_V>R*J zuu2De^)P%$JKr(XJ4L}}x^t`9p2VFLsah&&hP)R!%(Z(Nf{k?SsAZljHfN4s(Y2vD z!25HhU^`C;Sxd=BF|7FG1id!za#pkBmvWbcwdtoum{6$)R8Ktxk1JWqfO1trQbCXi?fh{O@6Y5_!~iEqe?_aFq+TPvhOWvyVpX{lTM6Ybs|Ib?nZW# zk>KKjzDVqTNO+WwM@kCKvl_gO2ke8$N<(BUix<^f@!RaIi4nu_EIUCRp?9f)x}L&a z{rQXSZ{)sha$z%jMKBy2K?S`I=k)@eB9O>36HO3(1HR5lBb$=sv8#&Nxf!#?NsPuJ zBSLmNubO6uDmZh0+)7#Y@Y>uU$iLN8r)o(o*mZ8_IDsW%8w189RKYkLiVW57k9_51 zvv|ql^XYv{CyjiVz9YCKIDIJeB7CC)SMdoYP7$#U7EUQJeJa?DuQPKDFTpb;{8+l_ zM-=wjLzf|C0T$2J-=yPx;9h+`cG*vV^Sl&@4g>%IA(E(~&R#(2fBt`waH&Up5#=#I QUiq&n$iieQrHlgp2f;SLtpET3 literal 0 HcmV?d00001 diff --git a/public/js/index.js b/public/js/index.js index 882f19e13d..d99457514b 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1576,6 +1576,7 @@ function initAdmin() { switch (provider) { case 'github': case 'gitlab': + case 'gitea': $('.oauth2_use_custom_url').show(); break; case 'openidConnect': @@ -1609,6 +1610,7 @@ function initAdmin() { $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input, .oauth2_email_url input').attr('required', 'required'); $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url, .oauth2_email_url').show(); break; + case 'gitea': case 'gitlab': $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input').attr('required', 'required'); $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url').show(); diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index 91d3cde308..bf33dedd19 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -110,6 +110,8 @@ {{.i18n.Tr "admin.auths.tip.twitter"}}
  • Discord
  • {{.i18n.Tr "admin.auths.tip.discord"}} +
  • Gitea
  • + {{.i18n.Tr "admin.auths.tip.gitea"}} diff --git a/vendor/github.com/markbates/goth/.travis.yml b/vendor/github.com/markbates/goth/.travis.yml index 5b2f9fe132..7fd29cde8d 100644 --- a/vendor/github.com/markbates/goth/.travis.yml +++ b/vendor/github.com/markbates/goth/.travis.yml @@ -8,6 +8,7 @@ go: - 1.9 - "1.10" - "1.11" + - "1.12" - tip matrix: diff --git a/vendor/github.com/markbates/goth/README.md b/vendor/github.com/markbates/goth/README.md index c74f8773c6..a35746e4e1 100644 --- a/vendor/github.com/markbates/goth/README.md +++ b/vendor/github.com/markbates/goth/README.md @@ -31,6 +31,7 @@ $ go get github.com/markbates/goth * Eve Online * Facebook * Fitbit +* Gitea * GitHub * Gitlab * Google @@ -41,13 +42,17 @@ $ go get github.com/markbates/goth * Intercom * Lastfm * Linkedin +* LINE +* Mailru * Meetup * MicrosoftOnline * Naver +* Nextcloud * OneDrive * OpenID Connect (auto discovery) * Paypal * SalesForce +* Shopify * Slack * Soundcloud * Spotify diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go index bea87d963d..ea4e1e0db8 100644 --- a/vendor/github.com/markbates/goth/gothic/gothic.go +++ b/vendor/github.com/markbates/goth/gothic/gothic.go @@ -245,19 +245,6 @@ var GetProviderName = getProviderName func getProviderName(req *http.Request) (string, error) { - // get all the used providers - providers := goth.GetProviders() - - // loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name - for _, provider := range providers { - p := provider.Name() - session, _ := Store.Get(req, p+SessionName) - value := session.Values[p] - if _, ok := value.(string); ok { - return p, nil - } - } - // try to get it from the url param "provider" if p := req.URL.Query().Get("provider"); p != "" { return p, nil @@ -278,6 +265,17 @@ func getProviderName(req *http.Request) (string, error) { return p, nil } + // As a fallback, loop over the used providers, if we already have a valid session for any provider (ie. user has already begun authentication with a provider), then return that provider name + providers := goth.GetProviders() + session, _ := Store.Get(req, SessionName) + for _, provider := range providers { + p := provider.Name() + value := session.Values[p] + if _, ok := value.(string); ok { + return p, nil + } + } + // if not found then return an empty string with the corresponding error return "", errors.New("you must select a provider") } diff --git a/vendor/github.com/markbates/goth/providers/gitea/gitea.go b/vendor/github.com/markbates/goth/providers/gitea/gitea.go new file mode 100644 index 0000000000..44887720c5 --- /dev/null +++ b/vendor/github.com/markbates/goth/providers/gitea/gitea.go @@ -0,0 +1,186 @@ +// Package gitea implements the OAuth2 protocol for authenticating users through gitea. +// This package can be used as a reference implementation of an OAuth2 provider for Goth. +package gitea + +import ( + "bytes" + "encoding/json" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "fmt" + "github.com/markbates/goth" + "golang.org/x/oauth2" +) + +// These vars define the default Authentication, Token, and Profile URLS for Gitea. +// +// Examples: +// gitea.AuthURL = "https://gitea.acme.com/oauth/authorize +// gitea.TokenURL = "https://gitea.acme.com/oauth/token +// gitea.ProfileURL = "https://gitea.acme.com/api/v3/user +var ( + AuthURL = "https://gitea.com/login/oauth/authorize" + TokenURL = "https://gitea.com/login/oauth/access_token" + ProfileURL = "https://gitea.com/api/v1/user" +) + +// Provider is the implementation of `goth.Provider` for accessing Gitea. +type Provider struct { + ClientKey string + Secret string + CallbackURL string + HTTPClient *http.Client + config *oauth2.Config + providerName string + authURL string + tokenURL string + profileURL string +} + +// New creates a new Gitea provider and sets up important connection details. +// You should always call `gitea.New` to get a new provider. Never try to +// create one manually. +func New(clientKey, secret, callbackURL string, scopes ...string) *Provider { + return NewCustomisedURL(clientKey, secret, callbackURL, AuthURL, TokenURL, ProfileURL, scopes...) +} + +// NewCustomisedURL is similar to New(...) but can be used to set custom URLs to connect to +func NewCustomisedURL(clientKey, secret, callbackURL, authURL, tokenURL, profileURL string, scopes ...string) *Provider { + p := &Provider{ + ClientKey: clientKey, + Secret: secret, + CallbackURL: callbackURL, + providerName: "gitea", + profileURL: profileURL, + } + p.config = newConfig(p, authURL, tokenURL, scopes) + return p +} + +// Name is the name used to retrieve this provider later. +func (p *Provider) Name() string { + return p.providerName +} + +// SetName is to update the name of the provider (needed in case of multiple providers of 1 type) +func (p *Provider) SetName(name string) { + p.providerName = name +} + +func (p *Provider) Client() *http.Client { + return goth.HTTPClientWithFallBack(p.HTTPClient) +} + +// Debug is a no-op for the gitea package. +func (p *Provider) Debug(debug bool) {} + +// BeginAuth asks Gitea for an authentication end-point. +func (p *Provider) BeginAuth(state string) (goth.Session, error) { + return &Session{ + AuthURL: p.config.AuthCodeURL(state), + }, nil +} + +// FetchUser will go to Gitea and access basic information about the user. +func (p *Provider) FetchUser(session goth.Session) (goth.User, error) { + sess := session.(*Session) + user := goth.User{ + AccessToken: sess.AccessToken, + Provider: p.Name(), + RefreshToken: sess.RefreshToken, + ExpiresAt: sess.ExpiresAt, + } + + if user.AccessToken == "" { + // data is not yet retrieved since accessToken is still empty + return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName) + } + + response, err := p.Client().Get(p.profileURL + "?access_token=" + url.QueryEscape(sess.AccessToken)) + if err != nil { + if response != nil { + response.Body.Close() + } + return user, err + } + + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode) + } + + bits, err := ioutil.ReadAll(response.Body) + if err != nil { + return user, err + } + + err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData) + if err != nil { + return user, err + } + + err = userFromReader(bytes.NewReader(bits), &user) + + return user, err +} + +func newConfig(provider *Provider, authURL, tokenURL string, scopes []string) *oauth2.Config { + c := &oauth2.Config{ + ClientID: provider.ClientKey, + ClientSecret: provider.Secret, + RedirectURL: provider.CallbackURL, + Endpoint: oauth2.Endpoint{ + AuthURL: authURL, + TokenURL: tokenURL, + }, + Scopes: []string{}, + } + + if len(scopes) > 0 { + for _, scope := range scopes { + c.Scopes = append(c.Scopes, scope) + } + } + return c +} + +func userFromReader(r io.Reader, user *goth.User) error { + u := struct { + Name string `json:"full_name"` + Email string `json:"email"` + NickName string `json:"login"` + ID int `json:"id"` + AvatarURL string `json:"avatar_url"` + }{} + err := json.NewDecoder(r).Decode(&u) + if err != nil { + return err + } + user.Email = u.Email + user.Name = u.Name + user.NickName = u.NickName + user.UserID = strconv.Itoa(u.ID) + user.AvatarURL = u.AvatarURL + return nil +} + +//RefreshTokenAvailable refresh token is provided by auth provider or not +func (p *Provider) RefreshTokenAvailable() bool { + return true +} + +//RefreshToken get new access token based on the refresh token +func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) { + token := &oauth2.Token{RefreshToken: refreshToken} + ts := p.config.TokenSource(goth.ContextForClient(p.Client()), token) + newToken, err := ts.Token() + if err != nil { + return nil, err + } + return newToken, err +} diff --git a/vendor/github.com/markbates/goth/providers/gitea/session.go b/vendor/github.com/markbates/goth/providers/gitea/session.go new file mode 100644 index 0000000000..18c3fff7e1 --- /dev/null +++ b/vendor/github.com/markbates/goth/providers/gitea/session.go @@ -0,0 +1,63 @@ +package gitea + +import ( + "encoding/json" + "errors" + "strings" + "time" + + "github.com/markbates/goth" +) + +// Session stores data during the auth process with Gitea. +type Session struct { + AuthURL string + AccessToken string + RefreshToken string + ExpiresAt time.Time +} + +var _ goth.Session = &Session{} + +// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Gitea provider. +func (s Session) GetAuthURL() (string, error) { + if s.AuthURL == "" { + return "", errors.New(goth.NoAuthUrlErrorMessage) + } + return s.AuthURL, nil +} + +// Authorize the session with Gitea and return the access token to be stored for future use. +func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) { + p := provider.(*Provider) + token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code")) + if err != nil { + return "", err + } + + if !token.Valid() { + return "", errors.New("Invalid token received from provider") + } + + s.AccessToken = token.AccessToken + s.RefreshToken = token.RefreshToken + s.ExpiresAt = token.Expiry + return token.AccessToken, err +} + +// Marshal the session into a string +func (s Session) Marshal() string { + b, _ := json.Marshal(s) + return string(b) +} + +func (s Session) String() string { + return s.Marshal() +} + +// UnmarshalSession wil unmarshal a JSON string into a session. +func (p *Provider) UnmarshalSession(data string) (goth.Session, error) { + s := &Session{} + err := json.NewDecoder(strings.NewReader(data)).Decode(s) + return s, err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7aa34911c8..c505448a41 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -303,13 +303,14 @@ github.com/magiconair/properties github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter -# github.com/markbates/goth v1.49.0 +# github.com/markbates/goth v1.56.0 github.com/markbates/goth github.com/markbates/goth/gothic github.com/markbates/goth/providers/bitbucket github.com/markbates/goth/providers/discord github.com/markbates/goth/providers/dropbox github.com/markbates/goth/providers/facebook +github.com/markbates/goth/providers/gitea github.com/markbates/goth/providers/github github.com/markbates/goth/providers/gitlab github.com/markbates/goth/providers/gplus