What is OAuth 2.0#
OAuth 2.0 provides a framework for granting limited access to resources without sharing credentials. It doesn’t confirm the user’s identity by itself. That’s why people often combine OAuth 2.0 with OpenID Connect (OIDC), which adds an identity layer on top of OAuth 2.0 for authentication.
It’s not an authentication protocol by itself (though often used with OpenID Connect for authentication).
It defines how a client application can request and obtain an access token from an authorization server, which can then be used to access protected resources on behalf of the user.
OAuth2 is specifically designed for authorization. It defines how a client can access a user’s resources, typically by obtaining tokens. OAuth2 usually uses JWTs, but it doesn’t have to.
sequenceDiagram
participant RO as Resource Owner (User)
participant Client as Client App
participant AS as Authorization Server
participant RS as Resource Server
Client->>RO: Authorization Request (redirect to login)
RO->>AS: Authenticate + Grant Permission
AS-->>Client: Authorization Grant (code)
Client->>AS: Exchange Grant for Access Token
AS-->>Client: Access Token (JWT)
Client->>RS: API Request with Access Token
RS-->>Client: Protected Resource
Concept |
Purpose |
Example |
|---|---|---|
Authentication |
Verifying who the user is |
“Log in with email/password.” |
Authorization |
Determining what the user/app is allowed to do |
“Allow app to access your Google Drive files.” |
JWT |
A token format used to store claims |
|
OAuth2 |
A framework for authorization |
Google login, GitHub OAuth apps |
Main Roles#
Resource Owner: The user who owns the data.Client: The application requesting access.Authorization Server: Issues tokens after verifying the user.Resource Server: Hosts the protected resources (e.g., APIs).
Core Flow#
Authorization Request: The client asks the user for permission.Authorization Grant: The user approves and the client gets a grant (e.g., code).Access Token Exchange: The client exchanges the grant for an access token.API Access: The client uses the token to call the resource server.
import requests
from requests_oauthlib import OAuth2Session
# OAuth 2.0 client details
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
authorization_base_url = "https://example.com/oauth/authorize"
token_url = "https://example.com/oauth/token"
redirect_uri = "https://localhost/callback"
# Step 1: Redirect user to provider for authorization
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri)
authorization_url, state = oauth.authorization_url(authorization_base_url)
print("Please go to this URL and authorize:", authorization_url)
# Step 2: Get the authorization response URL from user
redirect_response = input("Paste the full redirect URL here: ")
# Step 3: Fetch the access token
token = oauth.fetch_token(token_url, authorization_response=redirect_response,
client_secret=client_secret)
print("Access Token:", token)
# Step 4: Access a protected resource
protected_url = "https://example.com/api/user"
response = oauth.get(protected_url)
print("Protected Resource:", response.json())
Please go to this URL and authorize: https://example.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback&state=nukc5oHUnqmZbbWoWtaLOO39noQTbA
---------------------------------------------------------------------------
MismatchingStateError Traceback (most recent call last)
Cell In[3], line 20
17 redirect_response = input("Paste the full redirect URL here: ")
19 # Step 3: Fetch the access token
---> 20 token = oauth.fetch_token(token_url, authorization_response=redirect_response,
21 client_secret=client_secret)
22 print("Access Token:", token)
24 # Step 4: Access a protected resource
File ~/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages/requests_oauthlib/oauth2_session.py:271, in OAuth2Session.fetch_token(self, token_url, code, authorization_response, body, auth, username, password, method, force_querystring, timeout, headers, verify, proxies, include_client_id, client_secret, cert, **kwargs)
268 raise InsecureTransportError()
270 if not code and authorization_response:
--> 271 self._client.parse_request_uri_response(
272 authorization_response, state=self._state
273 )
274 code = self._client.code
275 elif not code and isinstance(self._client, WebApplicationClient):
File ~/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/clients/web_application.py:220, in WebApplicationClient.parse_request_uri_response(self, uri, state)
176 def parse_request_uri_response(self, uri, state=None):
177 """Parse the URI query for code and state.
178
179 If the resource owner grants the access request, the authorization
(...) 218 oauthlib.oauth2.rfc6749.errors.MismatchingStateError
219 """
--> 220 response = parse_authorization_code_response(uri, state=state)
221 self.populate_code_attributes(response)
222 return response
File ~/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/parameters.py:277, in parse_authorization_code_response(uri, state)
274 params = dict(urlparse.parse_qsl(query))
276 if state and params.get('state') != state:
--> 277 raise MismatchingStateError()
279 if 'error' in params:
280 raise_from_error(params.get('error'), params)
MismatchingStateError: (mismatching_state) CSRF Warning! State not equal in request and response.
Step-by-Step Minimal OAuth2 Server#
Collecting authlib
Downloading authlib-1.6.5-py2.py3-none-any.whl.metadata (9.8 kB)
Requirement already satisfied: starlette in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (0.48.0)
Requirement already satisfied: uvicorn in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (0.38.0)
Requirement already satisfied: cryptography in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from authlib) (46.0.3)
Requirement already satisfied: anyio<5,>=3.6.2 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from starlette) (4.11.0)
Requirement already satisfied: typing-extensions>=4.10.0 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from starlette) (4.15.0)
Requirement already satisfied: idna>=2.8 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from anyio<5,>=3.6.2->starlette) (3.11)
Requirement already satisfied: sniffio>=1.1 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from anyio<5,>=3.6.2->starlette) (1.3.1)
Requirement already satisfied: click>=7.0 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from uvicorn) (8.3.0)
Requirement already satisfied: h11>=0.8 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from uvicorn) (0.16.0)
Requirement already satisfied: cffi>=2.0.0 in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from cryptography->authlib) (2.0.0)
Requirement already satisfied: pycparser in /Users/hotronghai/Documents/Hai-Source/lectures-fs/.venv/lib/python3.12/site-packages (from cffi>=2.0.0->cryptography->authlib) (2.23)
Downloading authlib-1.6.5-py2.py3-none-any.whl (243 kB)
Installing collected packages: authlib
Successfully installed authlib-1.6.5