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