{"id":18405573,"url":"https://github.com/douglasmakey/oauth2-example","last_synced_at":"2025-03-17T07:29:35.975Z","repository":{"id":50283309,"uuid":"140487403","full_name":"douglasmakey/oauth2-example","owner":"douglasmakey","description":"A simple Oauth2 example with Go","archived":false,"fork":false,"pushed_at":"2024-02-28T16:54:59.000Z","size":31,"stargazers_count":153,"open_issues_count":1,"forks_count":35,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-06T03:04:02.334Z","etag":null,"topics":["article","example","go","golang","google","oauth2"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/douglasmakey.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-10T21:07:39.000Z","updated_at":"2024-11-04T20:37:34.000Z","dependencies_parsed_at":"2024-11-06T03:14:01.803Z","dependency_job_id":"c51e6df9-ed89-4cdd-b5ed-6a81c4868fe3","html_url":"https://github.com/douglasmakey/oauth2-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douglasmakey%2Foauth2-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douglasmakey%2Foauth2-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douglasmakey%2Foauth2-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douglasmakey%2Foauth2-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/douglasmakey","download_url":"https://codeload.github.com/douglasmakey/oauth2-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243991724,"owners_count":20380045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["article","example","go","golang","google","oauth2"],"created_at":"2024-11-06T03:02:05.499Z","updated_at":"2025-03-17T07:29:35.937Z","avatar_url":"https://github.com/douglasmakey.png","language":"HTML","readme":"# Oauth2-example with Go\n## Read the article in my blog [here](https://www.kungfudev.com/blog/2018/07/10/oauth2-example-with-go).\nAuthentication is the most common part in any application. You can implement your own authentication system or use one of the many alternatives that exist, but in this case we are going to use OAuth2.\n\nOAuth is a specification that allows users to delegate access to their data without sharing\ntheir username and password with that service, if you want to read more about Oauth2 go [here](https://oauth.net/2/).\n \n \n## Config Google Project\nFirst things first, we need to create our Google Project and create OAuth2 credentials.\n\n* Go to Google Cloud Platform\n* Create a new project or select one if you already have it.\n* Go to Credentials and then create a new one choosing  “OAuth client ID”\n* Add \"authorized redirect URL\", for this example `localhost:8000/auth/google/callback`\n* Copy the client_id and client secret\n\n\n## How OAuth2 works with Google\nThe authorization sequence begins when your application redirects the browser to a Google URL; the URL includes query parameters that indicate the type of access being requested. Google handles the user authentication, session selection, and user consent. The result is an authorization code, which the application can exchange for an access token and a refresh token.\n\nThe application should store the refresh token for future use and use the access token to access a Google API. Once the access token expires, the application uses the refresh token to obtain a new one.\n\n![Oauth2Google](https://www.kungfudev.com/images/blog/authorization-code.png)\n\n## Let's go to the code\nWe will use the package \"golang.org/x/oauth2\" that provides support for making OAuth2 authorized and authenticated HTTP requests.\n\nCreate a new project(folder) in your workdir in my case I will call it 'oauth2-example', and we need to include the package of oauth2.\n\n`go get golang.org/x/oauth2`\n\n\nSo into the project we create a main.go.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"log\"\n\t\"github.com/douglasmakey/oauth2-example/handlers\"\n)\n\nfunc main() {\n\tserver := \u0026http.Server{\n\t\tAddr: fmt.Sprintf(\":8000\"),\n\t\tHandler: handlers.New(),\n\t}\n\n\tlog.Printf(\"Starting HTTP Server. Listening at %q\", server.Addr)\n\tif err := server.ListenAndServe(); err != http.ErrServerClosed {\n\t\tlog.Printf(\"%v\", err)\n\t} else {\n\t\tlog.Println(\"Server closed!\")\n\t}\n}\n\n```\nWe create a simple server using http.Server and run.\n\nNext, we create folder 'handlers' that contains handler of our application, in this folder create 'base.go'.\n\n```go\npackage handlers\n\nimport (\n\t\"net/http\"\n)\n\nfunc New() http.Handler {\n\tmux := http.NewServeMux()\n\t// Root\n\tmux.Handle(\"/\",  http.FileServer(http.Dir(\"templates/\")))\n\n\t// OauthGoogle\n\tmux.HandleFunc(\"/auth/google/login\", oauthGoogleLogin)\n\tmux.HandleFunc(\"/auth/google/callback\", oauthGoogleCallback)\n\n\treturn mux\n}\n```\n\nWe use **http.ServeMux** to handle our endpoints, next we create the Root endpoint \"/\" for serving a simple template with a minimmum HTML\u0026CSS in this example we use 'http. http.FileServer', that template is 'index.html' and is in the folder 'templates'.\n\nAlso we create two endpoints for Oauth with Google \"/auth/google/login\" and \"/auth/google/callback\". Remember when we configured our application in the Google console? The callback url must be the same.\n\nNext, we create another file into handlers, we'll call it 'oauth_google.go', this file contains all logic to handle OAuth with Google in our application.\n\nWe Declare the var googleOauthConfig with auth.Config to communicate with Google.\nScopes: OAuth 2.0 scopes provide a way to limit the amount of access that is granted to an\naccess token.\n\n```go\nvar googleOauthConfig = \u0026oauth2.Config{\n\tRedirectURL:  \"http://localhost:8000/auth/google/callback\",\n\tClientID:     os.Getenv(\"GOOGLE_OAUTH_CLIENT_ID\"),\n\tClientSecret: os.Getenv(\"GOOGLE_OAUTH_CLIENT_SECRET\"),\n\tScopes:       []string{\"https://www.googleapis.com/auth/userinfo.email\"},\n\tEndpoint:     google.Endpoint,\n}\n```\n\n\n### Handler oauthGoogleLogin\n\nThis handler creates a login link and redirects the user to it:\n\n\nAuthCodeURL receive state that is a token to protect the user from CSRF attacks. You must\nalways provide a non-empty string and validate that it matches with the state query parameter on your redirect callback, It's advisable that this is randomly generated for each request, that's why we use a simple cookie.\n\t\n```go\nfunc oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {\n\n\t// Create oauthState cookie\n\toauthState := generateStateOauthCookie(w)\n\tu := googleOauthConfig.AuthCodeURL(oauthState)\n\thttp.Redirect(w, r, u, http.StatusTemporaryRedirect)\n}\n\nfunc generateStateOauthCookie(w http.ResponseWriter) string {\n\tvar expiration = time.Now().Add(365 * 24 * time.Hour)\n\n\tb := make([]byte, 16)\n\trand.Read(b)\n\tstate := base64.URLEncoding.EncodeToString(b)\n\tcookie := http.Cookie{Name: \"oauthstate\", Value: state, Expires: expiration}\n\thttp.SetCookie(w, \u0026cookie)\n\n\treturn state\n}\n\t\n```\n\n### Handler oauthGoogleCallback\n\nThis handler check if the state is equals to oauthStateCookie, and pass the code to the function **getUserDataFromGoogle**.\n\n```go\nfunc oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {\n\t// Read oauthState from Cookie\n\toauthState, _ := r.Cookie(\"oauthstate\")\n\n\tif r.FormValue(\"state\") != oauthState.Value {\n\t\tlog.Println(\"invalid oauth google state\")\n\t\thttp.Redirect(w, r, \"/\", http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\n\tdata, err := getUserDataFromGoogle(r.FormValue(\"code\"))\n\tif err != nil {\n\t\tlog.Println(err.Error())\n\t\thttp.Redirect(w, r, \"/\", http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\n\t// GetOrCreate User in your db.\n\t// Redirect or response with a token.\n\t// More code .....\n\tfmt.Fprintf(w, \"UserInfo: %s\\n\", data)\n}\n\nfunc getUserDataFromGoogle(code string) ([]byte, error) {\n\t// Use code to get token and get user info from Google.\n\t\n\ttoken, err := googleOauthConfig.Exchange(context.Background(), code)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"code exchange wrong: %s\", err.Error())\n\t}\n\tresponse, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed getting user info: %s\", err.Error())\n\t}\n\tdefer response.Body.Close()\n\tcontents, err := ioutil.ReadAll(response.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed read response: %s\", err.Error())\n\t}\n\treturn contents, nil\n}\n\n\n```\n\n### Full code oauth_google.go\n\n```go\npackage handlers\n\nimport (\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/google\"\n\t\"net/http\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"context\"\n\t\"log\"\n\t\"encoding/base64\"\n\t\"crypto/rand\"\n\t\"os\"\n\t\"time\"\n)\n\n// Scopes: OAuth 2.0 scopes provide a way to limit the amount of access that is granted to an access token.\nvar googleOauthConfig = \u0026oauth2.Config{\n\tRedirectURL:  \"http://localhost:8000/auth/google/callback\",\n\tClientID:     os.Getenv(\"GOOGLE_OAUTH_CLIENT_ID\"),\n\tClientSecret: os.Getenv(\"GOOGLE_OAUTH_CLIENT_SECRET\"),\n\tScopes:       []string{\"https://www.googleapis.com/auth/userinfo.email\"},\n\tEndpoint:     google.Endpoint,\n}\n\nconst oauthGoogleUrlAPI = \"https://www.googleapis.com/oauth2/v2/userinfo?access_token=\"\n\nfunc oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {\n\n\t// Create oauthState cookie\n\toauthState := generateStateOauthCookie(w)\n\n\t/*\n\tAuthCodeURL receive state that is a token to protect the user from CSRF attacks. You must always provide a non-empty string and\n\tvalidate that it matches the the state query parameter on your redirect callback.\n\t*/\n\tu := googleOauthConfig.AuthCodeURL(oauthState)\n\thttp.Redirect(w, r, u, http.StatusTemporaryRedirect)\n}\n\nfunc oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {\n\t// Read oauthState from Cookie\n\toauthState, _ := r.Cookie(\"oauthstate\")\n\n\tif r.FormValue(\"state\") != oauthState.Value {\n\t\tlog.Println(\"invalid oauth google state\")\n\t\thttp.Redirect(w, r, \"/\", http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\n\tdata, err := getUserDataFromGoogle(r.FormValue(\"code\"))\n\tif err != nil {\n\t\tlog.Println(err.Error())\n\t\thttp.Redirect(w, r, \"/\", http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\n\t// GetOrCreate User in your db.\n\t// Redirect or response with a token.\n\t// More code .....\n\tfmt.Fprintf(w, \"UserInfo: %s\\n\", data)\n}\n\nfunc generateStateOauthCookie(w http.ResponseWriter) string {\n\tvar expiration = time.Now().Add(365 * 24 * time.Hour)\n\n\tb := make([]byte, 16)\n\trand.Read(b)\n\tstate := base64.URLEncoding.EncodeToString(b)\n\tcookie := http.Cookie{Name: \"oauthstate\", Value: state, Expires: expiration}\n\thttp.SetCookie(w, \u0026cookie)\n\n\treturn state\n}\n\nfunc getUserDataFromGoogle(code string) ([]byte, error) {\n\t// Use code to get token and get user info from Google.\n\n\ttoken, err := googleOauthConfig.Exchange(context.Background(), code)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"code exchange wrong: %s\", err.Error())\n\t}\n\tresponse, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed getting user info: %s\", err.Error())\n\t}\n\tdefer response.Body.Close()\n\tcontents, err := ioutil.ReadAll(response.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed read response: %s\", err.Error())\n\t}\n\treturn contents, nil\n}\n\n```\n## let's run and test\n```bash\ngo run main.go\n```\n","funding_links":[],"categories":["HTML"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdouglasmakey%2Foauth2-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdouglasmakey%2Foauth2-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdouglasmakey%2Foauth2-example/lists"}