@@ -207,3 +207,38 @@ func (cs computeSource) Token() (*oauth2.Token, error) {
207207 "oauth2.google.serviceAccount" : acct ,
208208 }), nil
209209}
210+
211+ // AuthorizationHandler is a 3-legged-OAuth helper that
212+ // prompts the user for OAuth consent at the specified Auth URL
213+ // and returns an auth code and state upon approval.
214+ type AuthorizationHandler func (string ) (string , string , error )
215+
216+ // OAuthClientTokenSource returns an oauth2.TokenSource that fetches access tokens
217+ // using 3-legged-OAuth workflow.
218+ // The provided oauth2.Config should be a full configuration containing AuthURL,
219+ // TokenURL, and scope.
220+ // An environment-specific AuthorizationHandler is used to obtain user consent.
221+ // Per OAuth protocol, a unique "state" string should be sent and verified
222+ // before token exchange to prevent CSRF attacks.
223+ func OAuthClientTokenSource (config oauth2.Config , ctx context.Context , authHandler AuthorizationHandler , state string ) oauth2.TokenSource {
224+ return oauth2 .ReuseTokenSource (nil , oauthClientSource {config : config , ctx : ctx , authHandler : authHandler , state : state })
225+ }
226+
227+ type oauthClientSource struct {
228+ config oauth2.Config
229+ ctx context.Context
230+ authHandler AuthorizationHandler
231+ state string
232+ }
233+
234+ func (ocs oauthClientSource ) Token () (* oauth2.Token , error ) {
235+ url := ocs .config .AuthCodeURL (ocs .state )
236+ code , state , err := ocs .authHandler (url )
237+ if err != nil {
238+ return nil , err
239+ }
240+ if state == ocs .state {
241+ return ocs .config .Exchange (ocs .ctx , code )
242+ }
243+ return nil , errors .New ("State mismatch in OAuth workflow." )
244+ }
0 commit comments