github.com/go-swagger/go-swagger@v0.31.0/examples/oauth2/restapi/implementation.go (about) 1 package restapi 2 3 // THIS CODE HAS NOT BEEN GENERATED 4 5 import ( 6 "errors" 7 "fmt" 8 "io" 9 "log" 10 "net/http" 11 12 "context" 13 14 oidc "github.com/coreos/go-oidc/v3/oidc" 15 "github.com/go-openapi/runtime" 16 "github.com/go-openapi/runtime/middleware" 17 "golang.org/x/oauth2" 18 ) 19 20 var ( 21 // state carries an internal token during the oauth2 workflow 22 // we just need a non empty initial value 23 state = "foobar" // Don't make this a global in production. 24 25 // the credentials for this API (adapt values when registering API) 26 clientID = "" // <= enter registered API client ID here 27 clientSecret = "" // <= enter registered API client secret here 28 29 // unused in this example: the signer of the delivered token 30 // issuer = "https://accounts.google.com" 31 32 // the Google login URL 33 authURL = "https://accounts.google.com/o/oauth2/v2/auth" 34 35 // the Google OAuth2 resource provider which delivers access tokens 36 /* #nosec */ 37 tokenURL = "https://www.googleapis.com/oauth2/v4/token" 38 userInfoURL = "https://www.googleapis.com/oauth2/v3/userinfo" 39 40 // our endpoint to be called back by the redirected client 41 callbackURL = "http://127.0.0.1:12345/api/auth/callback" 42 43 // the description of the OAuth2 flow 44 endpoint = oauth2.Endpoint{ 45 AuthURL: authURL, 46 TokenURL: tokenURL, 47 } 48 49 config = oauth2.Config{ 50 ClientID: clientID, 51 ClientSecret: clientSecret, 52 Endpoint: endpoint, 53 RedirectURL: callbackURL, 54 Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, 55 } 56 ) 57 58 func login(r *http.Request) middleware.Responder { 59 // implements the login with a redirection 60 return middleware.ResponderFunc( 61 func(w http.ResponseWriter, pr runtime.Producer) { 62 http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound) 63 }) 64 } 65 66 func callback(r *http.Request) (string, error) { 67 // we expect the redirected client to call us back 68 // with 2 query params: state and code. 69 // We use directly the Request params here, since we did not 70 // bother to document these parameters in the spec. 71 72 if r.URL.Query().Get("state") != state { 73 log.Println("state did not match") 74 return "", errors.New("state did not match") 75 } 76 77 myClient := &http.Client{} 78 79 parentContext := context.Background() 80 ctx := oidc.ClientContext(parentContext, myClient) 81 82 authCode := r.URL.Query().Get("code") 83 log.Printf("Authorization code: %v\n", authCode) 84 85 // Exchange converts an authorization code into a token. 86 // Under the hood, the oauth2 client POST a request to do so 87 // at tokenURL, then redirects... 88 oauth2Token, err := config.Exchange(ctx, authCode) 89 if err != nil { 90 log.Println("failed to exchange token", err.Error()) 91 return "", errors.New("failed to exchange token") 92 } 93 94 // the authorization server's returned token 95 log.Println("Raw token data:", oauth2Token) 96 return oauth2Token.AccessToken, nil 97 } 98 99 func authenticated(token string) (bool, error) { 100 // validates the token by sending a request at userInfoURL 101 bearToken := "Bearer " + token 102 req, err := http.NewRequest("GET", userInfoURL, nil) 103 if err != nil { 104 return false, fmt.Errorf("http request: %v", err) 105 } 106 107 req.Header.Add("Authorization", bearToken) 108 109 cli := &http.Client{} 110 resp, err := cli.Do(req) 111 if err != nil { 112 return false, fmt.Errorf("http request: %v", err) 113 } 114 defer resp.Body.Close() 115 116 _, err = io.ReadAll(resp.Body) 117 if err != nil { 118 return false, fmt.Errorf("fail to get response: %v", err) 119 } 120 if resp.StatusCode != 200 { 121 return false, nil 122 } 123 return true, nil 124 }