Complete implementation guide for OAuth 2.0 Authorization Framework with multi-tenancy support. Use when implementing or validating rfc 6749 oauth2 protocol compliance.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill rfc-6749-oauth2 --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Rfc 6749 Oauth2?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-rfc-6749-oauth2)More formats (shields.io, HTML) on the badges page.
---
name: rfc-6749-oauth2
description: Complete implementation guide for OAuth 2.0 Authorization Framework with multi-tenancy support. Use when implementing or validating rfc 6749 oauth2 protocol compliance.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: rfc
tags: [oauth, authorization, identity, multi-tenancy]
---
# RFC 6749 - OAuth 2.0 Authorization Framework
You are an expert in implementing the OAuth 2.0 Authorization Framework as defined in RFC 6749. You provide secure, standards-compliant implementations with multi-tenancy considerations.
## RFC Overview
| Property | Value |
| ---------- | ------------------------------------- |
| RFC Number | 6749 |
| Title | The OAuth 2.0 Authorization Framework |
| Status | Proposed Standard |
| Published | October 2012 |
| Updated By | RFC 8252, RFC 9700 |
## Protocol Flow
```
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
```
## Grant Types
### Authorization Code Grant (Recommended)
```
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
```
### Implementation
```python
# authorization_server.py
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Optional
import secrets
import hashlib
import base64
from urllib.parse import urlencode, parse_qs
@dataclass
class AuthorizationCode:
code: str
client_id: str
redirect_uri: str
scope: str
user_id: str
tenant_id: str # Multi-tenancy support
code_challenge: Optional[str] # PKCE
code_challenge_method: Optional[str]
expires_at: datetime
used: bool = False
@dataclass
class AccessToken:
token: str
token_type: str
client_id: str
user_id: str
tenant_id: str
scope: str
expires_at: datetime
@dataclass
class RefreshToken:
token: str
client_id: str
user_id: str
tenant_id: str
scope: str
expires_at: datetime
revoked: bool = False
class OAuth2AuthorizationServer:
"""RFC 6749 compliant Authorization Server with multi-tenancy."""
def __init__(self, tenant_id: str):
self.tenant_id = tenant_id
self.authorization_codes: dict[str, AuthorizationCode] = {}
self.access_tokens: dict[str, AccessToken] = {}
self.refresh_tokens: dict[str, RefreshToken] = {}
self.clients: dict[str, dict] = {}
def register_client(
self,
client_id: str,
client_secret: Optional[str],
redirect_uris: list[str],
grant_types: list[str],
client_type: str = "confidential" # or "public"
) -> dict:
"""
Register an OAuth 2.0 client.
RFC 6749 Section 2: Client Registration
"""
client = {
"client_id": client_id,
"client_secret": client_secret,
"redirect_uris": redirect_uris,
"grant_types": grant_types,
"client_type": client_type,
"tenant_id": self.tenant_id,
}
self.clients[client_id] = client
return client
def authorize(
self,
response_type: str,
client_id: str,
redirect_uri: str,
scope: str,
state: str,
user_id: str,
code_challenge: Optional[str] = None,
code_challenge_method: Optional[str] = None,
) -> str:
"""
Authorization Endpoint.
RFC 6749 Section 4.1.1: Authorization Request
"""
# Validate client
client = self.clients.get(client_id)
if not client:
raise OAuth2Error("invalid_client", "Unknown client")
# Validate redirect_uri
if redirect_uri not in client["redirect_uris"]:
raise OAuth2Error("invalid_request", "Invalid redirect_uri")
# Validate response_type
if response_type != "code":
return self._build_error_redirect(
redirect_uri, "unsupported_response_type", state
)
# Generate authorization code
code = secrets.token_urlsafe(32)
self.authorization_codes[code] = AuthorizationCode(
code=code,
client_id=client_id,
redirect_uri=redirect_uri,
scope=scope,
user_id=user_id,
tenant_id=self.tenant_id,
code_challenge=code_challenge,
code_challenge_method=code_challenge_method,
expires_at=datetime.utcnow() + timedelta(minutes=10),
)
# Build redirect with code
params = {"code": code, "state": state}
return f"{redirect_uri}?{urlencode(params)}"
def token(
self,
grant_type: str,
client_id: str,
client_secret: Optional[str] = None,
code: Optional[str] = None,
redirect_uri: Optional[str] = None,
refresh_token: Optional[str] = None,
code_verifier: Optional[str] = None,
scope: Optional[str] = None,
) -> dict:
"""
Token Endpoint.
RFC 6749 Section 4.1.3: Access Token Request
"""
# Authenticate client
client = self._authenticate_client(client_id, client_secret)
if grant_type == "authorization_code":
return self._handle_authorization_code_grant(
client, code, redirect_uri, code_verifier
)
elif grant_type == "refresh_token":
return self._handle_refresh_token_grant(
client, refresh_token, scope
)
elif grant_type == "client_credentials":
return self._handle_client_credentials_grant(client, scope)
else:
raise OAuth2Error("unsupported_grant_type")
def _handle_authorization_code_grant(
self,
client: dict,
code: str,
redirect_uri: str,
code_verifier: Optional[str],
) -> dict:
"""RFC 6749 Section 4.1.3 + RFC 7636 PKCE"""
auth_code = self.authorization_codes.get(code)
# Validate authorization code
if not auth_code:
raise OAuth2Error("invalid_grant", "Invalid authorization code")
if auth_code.used:
# Revoke all tokens if code reuse detected (security)
self._revoke_tokens_for_code(code)
raise OAuth2Error("invalid_grant", "Authorization code already used")
if auth_code.expires_at < datetime.utcnow():
raise OAuth2Error("invalid_grant", "Authorization code expired")
if auth_code.client_id != client["client_id"]:
raise OAuth2Error("invalid_grant", "Client mismatch")
if auth_code.redirect_uri != redirect_uri:
raise OAuth2Error("invalid_grant", "Redirect URI mismatch")
# PKCE verification (RFC 7636)
if auth_code.code_challenge:
if not code_verifier:
raise OAuth2Error("invalid_grant", "Code verifier required")
if not self._verify_pkce(
code_verifier,
auth_code.code_challenge,
auth_code.code_challenge_method,
):
raise OAuth2Error("invalid_grant", "PKCE verification failed")
# Mark code as used
auth_code.used = True
# Generate tokens
return self._issue_tokens(
client["client_id"],
auth_code.user_id,
auth_code.scope,
)
def _handle_refresh_token_grant(
self,
client: dict,
token: str,
scope: Optional[str],
) -> dict:
"""RFC 6749 Section 6: Refreshing an Access Token"""
refresh = self.refresh_tokens.get(token)
if not refresh:
raise OAuth2Error("invalid_grant", "Invalid refresh token")
if refresh.revoked:
raise OAuth2Error("invalid_grant", "Refresh token revoked")
if refresh.expires_at < datetime.utcnow():
raise OAuth2Error("invalid_grant", "Refresh token expired")
if refresh.client_id != client["client_id"]:
raise OAuth2Error("invalid_grant", "Client mismatch")
# Scope validation - can only narrow, not expand
if scope:
requested_scopes = set(scope.split())
original_scopes = set(refresh.scope.split())
if not requested_scopes.issubset(original_scopes):
raise OAuth2Error("invalid_scope")
final_scope = scope
else:
final_scope = refresh.scope
# Rotate refresh token (security best practice)
refresh.revoked = True
return self._issue_tokens(
client["client_id"],
refresh.user_id,
final_scope,
)
def _handle_client_credentials_grant(
self,
client: dict,
scope: Optional[str],
) -> dict:
"""RFC 6749 Section 4.4: Client Credentials Grant"""
if client["client_type"] != "confidential":
raise OAuth2Error("unauthorized_client")
return self._issue_tokens(
client["client_id"],
None, # No user in client credentials
scope or "",
include_refresh=False,
)
def _issue_tokens(
self,
client_id: str,
user_id: Optional[str],
scope: str,
include_refresh: bool = True,
) -> dict:
"""Generate access and refresh tokens."""
access_token = secrets.token_urlsafe(32)
self.access_tokens[access_token] = AccessToken(
token=access_token,
token_type="Bearer",
client_id=client_id,
user_id=user_id,
tenant_id=self.tenant_id,
scope=scope,
expires_at=datetime.utcnow() + timedelta(hours=1),
)
response = {
"access_token": access_token,
"token_type": "Bearer",
"expires_in": 3600,
"scope": scope,
}
if include_refresh and user_id:
refresh_token = secrets.token_urlsafe(32)
self.refresh_tokens[refresh_token] = RefreshToken(
token=refresh_token,
client_id=client_id,
user_id=user_id,
tenant_id=self.tenant_id,
scope=scope,
expires_at=datetime.utcnow() + timedelta(days=30),
)
response["refresh_token"] = refresh_token
return response
def _authenticate_client(
self,
client_id: str,
client_secret: Optional[str],
) -> dict:
"""Authenticate the client."""
client = self.clients.get(client_id)
if not client:
raise OAuth2Error("invalid_client")
# Public clients don't require secret
if client["client_type"] == "public":
return client
# Confidential clients must authenticate
if not secrets.compare_digest(
client.get("client_secret", ""),
client_secret or "",
):
raise OAuth2Error("invalid_client")
return client
def _verify_pkce(
self,
code_verifier: str,
code_challenge: str,
method: str,
) -> bool:
"""Verify PKCE code verifier against challenge."""
if method == "S256":
computed = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip("=")
return secrets.compare_digest(computed, code_challenge)
elif method == "plain":
return secrets.compare_digest(code_verifier, code_challenge)
return False
def _build_error_redirect(
self,
redirect_uri: str,
error: str,
state: str,
) -> str:
params = {"error": error, "state": state}
return f"{redirect_uri}?{urlencode(params)}"
class OAuth2Error(Exception):
"""OAuth 2.0 Error Response (RFC 6749 Section 5.2)"""
def __init__(self, error: str, description: str = None):
self.error = error
self.description = description
def to_response(self) -> dict:
response = {"error": self.error}
if self.description:
response["error_description"] = self.description
return response
```
## Multi-Tenancy Considerations
### Tenant Isolation
```python
class MultiTenantOAuth2Server:
"""OAuth 2.0 server with tenant isolation."""
def __init__(self):
self.tenant_servers: dict[str, OAuth2AuthorizationServer] = {}
def get_tenant_server(self, tenant_id: str) -> OAuth2AuthorizationServer:
"""Get or create tenant-specific OAuth server."""
if tenant_id not in self.tenant_servers:
self.tenant_servers[tenant_id] = OAuth2AuthorizationServer(tenant_id)
return self.tenant_servers[tenant_id]
def validate_token(self, access_token: str, tenant_id: str) -> Optional[dict]:
"""Validate token with tenant isolation."""
server = self.get_tenant_server(tenant_id)
token = server.access_tokens.get(access_token)
if not token:
return None
if token.tenant_id != tenant_id:
return None # Cross-tenant access denied
if token.expires_at < datetime.utcnow():
return None
return {
"active": True,
"client_id": token.client_id,
"user_id": token.user_id,
"tenant_id": token.tenant_id,
"scope": token.scope,
"exp": int(token.expires_at.timestamp()),
}
```
### Tenant-Aware Endpoints
```python
from fastapi import FastAPI, Request, Depends, HTTPException
from fastapi.security import HTTPBearer
app = FastAPI()
security = HTTPBearer()
mt_server = MultiTenantOAuth2Server()
def get_tenant_id(request: Request) -> str:
"""Extract tenant from subdomain or header."""
# Option 1: Subdomain
host = request.headers.get("host", "")
if "." in host:
return host.split(".")[0]
# Option 2: Header
tenant = request.headers.get("X-Tenant-ID")
if tenant:
return tenant
raise HTTPException(400, "Tenant not specified")
@app.post("/oauth/authorize")
async def authorize(
request: Request,
tenant_id: str = Depends(get_tenant_id),
):
server = mt_server.get_tenant_server(tenant_id)
# ... authorization logic
@app.post("/oauth/token")
async def token(
request: Request,
tenant_id: str = Depends(get_tenant_id),
):
server = mt_server.get_tenant_server(tenant_id)
# ... token logic
```
## Security Considerations
### From RFC 6749 Section 10
1. **Client Authentication** - Use strong client secrets, prefer mTLS
2. **Client Impersonation** - Validate redirect_uri strictly
3. **Access Token Scope** - Minimize scope, use least privilege
4. **Refresh Token Protection** - Bind to client, rotate on use
5. **Authorization Code Injection** - Use PKCE (RFC 7636)
6. **Cross-Site Request Forgery** - Use state parameter
### Security Best Practices
```python
# Always use PKCE for all clients
def generate_pkce():
code_verifier = secrets.token_urlsafe(64)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip("=")
return code_verifier, code_challenge
# Use secure token storage
from cryptography.fernet import Fernet
class SecureTokenStore:
def __init__(self, key: bytes):
self.cipher = Fernet(key)
def store_token(self, token: str) -> bytes:
return self.cipher.encrypt(token.encode())
def retrieve_token(self, encrypted: bytes) -> str:
return self.cipher.decrypt(encrypted).decode()
```
## Related RFCs
| RFC | Title | Relationship |
| -------- | ------------------- | ----------------------------- |
| RFC 6750 | Bearer Token Usage | How to use access tokens |
| RFC 7636 | PKCE | Security extension (required) |
| RFC 7662 | Token Introspection | Token validation |
| RFC 9700 | Security BCP | Security requirements |
## Output Format
Provide:
- RFC-compliant OAuth 2.0 implementations
- Multi-tenancy patterns with isolation
- Security configurations
- Token management strategies
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!