Sitemap

Implementing oauth in Go without using oauth library

4 min readJul 31, 2026

You must’ve implemented OAuth 2.0 in Go before. And if you haven’t, no worries. In this blog, I’m implementing OAuth 2.0 without using any OAuth library, so we understand what’s actually happening behind the scenes instead of treating it like magic.

Let’s first understand what OAuth 2.0 actually is.

OAuth 2.0 is simply an authorization protocol. It allows a third-party application to access a user’s resources with limited permissions, without ever knowing the user’s password or sensitive credentials.

Every time you click “Sign in with Google”, “Continue with GitHub”, or “Login with Facebook”, you’re using OAuth 2.0.

From the user’s perspective, the flow looks incredibly simple. You click Login with Google, Google asks whether you want to allow the application to access some of your information, you click Allow, and you’re logged in.

But what actually happens behind the scenes?

Let’s walk through the complete flow.

There are three main entities involved in this flow.

  • Resource Owner — The user who owns the data.
  • Client — Your application that wants to access the data.
  • Authorization Server — Google (or GitHub, Facebook, etc.), which authenticates the user and issues tokens.

Before any user can log in, your application must first register itself with the authorization server. During registration, you’re provided with a Client ID and a Client Secret, and you configure a Redirect URI.

The redirect URI is simply the URL where the authorization server should send the user once they either approve or deny the request.

These credentials help the authorization server recognize your application whenever it receives an authorization request.

Now suppose the user clicks “Login with Google.”

Your server redirects the browser to Google’s authorization endpoint.

authURL := "https://accounts.google.com/o/oauth2/v2/auth"

params := url.Values{}
params.Add("client_id", "YOUR_CLIENT_ID")
params.Add("redirect_uri", "http://localhost:8080/callback")
params.Add("response_type", "code")
params.Add("scope", "email profile")
params.Add("state", "random_string")

http.Redirect(w, r, authURL+"?"+params.Encode(), http.StatusFound)

The request contains several parameters.

The Client ID identifies your application.

The Redirect URI tells Google where to send the user after authorization.

The Response Type is set to code, which tells Google that your application is requesting an authorization code.

The State is a random string used to prevent CSRF attacks. When the user returns from Google, your application verifies that the returned state matches the one it originally sent. If it doesn’t match, the request is rejected.

Finally, there’s the Scope.

The scope defines what data your application wants permission to access.

For example, if you’re building a meeting scheduler, you probably only need access to the user’s calendar. Your application doesn’t need their Drive files, Photos, or Contacts.

That’s exactly what scopes are for — they limit the permissions being requested.

Once the browser reaches Google, the user logs in if they aren’t already authenticated.

Google then shows a consent screen asking whether the user wants to allow your application to access the requested information.

Get Vishakha Sainani’s stories in your inbox

Join Medium for free to get updates from this writer.

If the user clicks Allow, Google redirects them back to your callback URL

http://localhost:8080/callback?code=abcxyz&state=random_string

Notice something important here.

Google doesn’t send an access token immediately.

Instead, it sends an authorization code.

Why?

Because anyone can inspect browser traffic. If Google directly returned an access token, an attacker intercepting the redirect could immediately start making API calls.

Instead, Google returns a short-lived, one-time authorization code. That code is useless unless your backend exchanges it for an access token.

Once your server receives the authorization code, it sends a POST request to Google’s token endpoint.

values := url.Values{}
values.Set("client_id", "YOUR_CLIENT_ID")
values.Set("client_secret", "YOUR_CLIENT_SECRET")
values.Set("code", code)
values.Set("redirect_uri", "http://localhost:8080/callback")
values.Set("grant_type", "authorization_code")

req, _ := http.NewRequest(
"POST",
"https://oauth2.googleapis.com/token",
strings.NewReader(values.Encode()),
)

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

The request contains five important pieces of information:

  • Client ID — identifies your application.
  • Client Secret — proves that the request is coming from your backend.
  • Authorization Code — the temporary code received from Google.
  • Redirect URI — must match the one used during the authorization request.
  • Grant Type — tells Google that this is an Authorization Code flow.

Google now performs several validations.

It checks whether the client ID is valid, whether the client secret matches, whether the authorization code exists, whether it has expired, whether it has already been used, and whether the redirect URI matches the one that was originally sent.

If all these checks pass, Google returns an access token.

{
"access_token": "ya29.a0AfH6SM...",
"expires_in": 3600,
"token_type": "Bearer"
}

Your application can now use this access token to access the user’s resources.

For example, if you want the user’s profile, your server simply includes the token in the Authorization header.

req, _ := http.NewRequest(
"GET",
"https://www.googleapis.com/oauth2/v2/userinfo",
nil,
)

req.Header.Set("Authorization", "Bearer "+accessToken)

Google validates the token and, if it’s still valid, returns the requested resource.

And that’s it.

The user never shared their password with your application.

Your application only received permission to access the specific resources defined by the requested scopes.

That’s the beauty of OAuth 2.0.

Instead of applications asking users for their passwords, the authorization server acts as the trusted middleman. It authenticates the user, asks for their consent, and issues a limited-access token that your application can use securely.

The next time you click “Sign in with Google”, you’ll know exactly what’s happening behind the scenes.