This project is an example OAuth 2.0 and OpenID Connect provider implemented in Go using the ory/fosite library (v0.49.0+).
It provides basic OAuth 2.0 flows (Authorization Code, Client Credentials, Refresh Token) and includes simple HTML templates for user login and consent.
- Go: Version 1.18 or higher. Ensure the Go executable is in your system's PATH or use the full path (e.g.,
/usr/local/go/bin/go).
- Clone the repository:
git clone <your-repository-url> cd <repository-directory>
- Download Dependencies:
/usr/local/go/bin/go mod download
This example uses hardcoded configuration values and compose.Compose for setting up Fosite.
- Fosite Setup: Uses
compose.Composewithcompose.CommonStrategy(main.go). - HMAC Secret: A 32-byte secret (
jwtSecret) is defined and set infositeConfig.GlobalSecret. The core HMAC strategy (CoreStrategy) is configured viacompose.NewOAuth2HMACStrategy(fositeConfig), relying on theGlobalSecret. Use a strong, random secret in production. - OIDC Strategy: Uses RSA keys (generated on startup) configured via
compose.NewOpenIDConnectStrategy(...)withinCommonStrategy. Use persistent, securely stored RSA keys in production. - Storage: Uses an in-memory store (
storage.go). All data is lost on restart. A genericStorageInterfaceis now available for implementing persistent storage alternatives. An example SQL adapter is provided indb_adapter.goto demonstrate how legacy tables can be mapped to the interface. Replace with persistent storage (SQL, etc.) for production. - Example Client: Client ID
my-test-client, secretfoobar(hashed instorage.go). - Port: Listens on
:8080(main.go). - Session Management: Basic, insecure in-memory sessions (
handlers.go). Replace with robust session handling. - CSRF Protection: Currently uses manual token checking. The
gorilla/csrfimport is commented out. Needs proper middleware implementation.
/usr/local/go/bin/go run .The server will start listening on http://localhost:8080.
Alternatively, you can build and run the application using Docker, avoiding the need to install Go dependencies locally.
-
Build the Docker Image:
docker build -t identity-go-app .This command builds the Docker image using the
Dockerfilein the root directory and tags it asidentity-go-app. -
Run the Docker Container:
docker run -p 8080:8080 identity-go-app
This command starts a container from the
identity-go-appimage and maps port 8080 on your host machine to port 8080 inside the container.The server will start listening, and you can access it at
http://localhost:8080in your browser.
/oauth2/auth: Authorization endpoint./oauth2/token: Token endpoint./oauth2/introspect: Token introspection endpoint./oauth2/revoke: Token revocation endpoint./login: Login form./consent: Consent form./.well-known/openid-configuration: (Currently Commented Out)./.well-known/jwks.json: (Currently Commented Out).
These examples use curl to interact with the API endpoints. Replace placeholders like <CODE>, <ACCESS_TOKEN>, <REFRESH_TOKEN>.
Note: /oauth2/auth, /login, /consent require browser interaction.
Open in browser:
http://localhost:8080/oauth2/auth?response_type=code&client_id=my-test-client&redirect_uri=http://localhost:3000/callback&scope=openid%20profile%20email%20offline&state=some-random-state-123
(After login/consent, browser redirects to http://localhost:3000/callback?code=<CODE>&state=...)
Exchange the CODE from step 1:
curl -X POST http://localhost:8080/oauth2/token \
-u "my-test-client:foobar" \
-d "grant_type=authorization_code" \
-d "code=<CODE>" \
-d "redirect_uri=http://localhost:3000/callback"Example Success Response (JSON):
{
"access_token": "...",
"expires_in": 1800,
"id_token": "...",
"refresh_token": "...",
"scope": "openid profile email offline",
"token_type": "bearer"
}Request an access token using client credentials.
curl -X POST http://localhost:8080/oauth2/token \
-u "my-test-client:foobar" \
-d "grant_type=client_credentials" \
-d "scope=openid profile"Example Success Response (JSON):
{
"access_token": "ory_at_...",
"expires_in": 1799,
"scope": "openid profile",
"token_type": "bearer"
}(Note: The exact access_token value will vary. Scope might differ based on client configuration.)
curl -X POST http://localhost:8080/oauth2/token \
-u "my-test-client:foobar" \
-d "grant_type=refresh_token" \
-d "refresh_token=<REFRESH_TOKEN>"Example Success Response (JSON):
{
"access_token": "(new)...",
"expires_in": 1800,
"id_token": "(potentially new)...",
"refresh_token": "(potentially new)...",
"scope": "openid profile email offline",
"token_type": "bearer"
}curl -X POST http://localhost:8080/oauth2/introspect \
-u "my-test-client:foobar" \
-d "token=<ACCESS_TOKEN>"Example Success Response (Active - JSON):
{
"active": true,
"aud": ["https://my-api.com"],
"client_id": "my-test-client",
"exp": ..., "iat": ..., "iss": "http://localhost:8080", "jti": "...", "nbf": ...,
"scope": "openid profile email offline",
"sub": "user-id...",
"token_type": "access_token",
"username": "user-id..."
}Example Success Response (Inactive - JSON):
{"active": false}curl -X POST http://localhost:8080/oauth2/revoke \
-u "my-test-client:foobar" \
-d "token=<ACCESS_TOKEN_OR_REFRESH_TOKEN>"Example Success Response (Status 200 OK - No Content)
Unit tests are provided in main_test.go. You can run them using:
/usr/local/go/bin/go test -vCurrent Test Coverage (Happy Paths):
- Client Credentials Grant (
TestClientCredentialsFlow) - Authorization Code Token Exchange (
TestAuthorizationCodeTokenExchange) - Token Introspection (Successful) (
TestTokenIntrospection) - Login Form Submission (
TestLoginHandler) - Consent Form Submission (Allow) (
TestConsentHandler)
Testing TODOs & Limitations:
- Full Authorization Code Flow: Test the complete sequence including redirects and handler interactions.
- Refresh Token Flow: Add test for the
refresh_tokengrant type. - Token Revocation Verification: The current
TestTokenRevocationpasses but confirms revocation is broken with the currentInMemoryStore(token not deleted). Needs a test that verifies successful revocation (requires fixing storage). - Introspection Failure Cases: Test introspection for invalid, expired, or properly revoked tokens (
active: false). - Login/Consent GET Handlers: Test rendering of the HTML forms.
- Login/Consent Failure Cases: Test invalid credentials, invalid CSRF token, consent denial.
- API Error Handling: Test invalid requests (client ID/secret, code, redirect URI, scope) to API endpoints.
- Isolate Global State: Refactor tests/session handling to avoid relying on the global
sessionsmap. - CSRF Middleware Testing: Update tests after implementing
gorilla/csrf. - OIDC/JWKS/PKCE Testing: Add tests when these features are uncommented/implemented.
- Storage Interactions: More detailed tests focusing specifically on the storage methods could be added, especially when moving to persistent storage.
- In-Memory Storage: Not suitable for production. A generic
StorageInterfacehas been implemented to make it easier to create persistent storage implementations (SQL, Redis, etc.). - Basic Session Handling: Insecure.
- No Proper CSRF Middleware: Manual CSRF logic in place;
gorilla/csrfnot used. - OIDC Discovery & JWKS Missing: Handlers are commented out.
- Hardcoded Configuration: Secrets and client details are hardcoded.
- Limited User Management: Only a dummy user (
user/password) check exists.
Future work includes:
- Implementing a persistent storage solution using the new
StorageInterface - Improving security with proper session management
- Enabling proper CSRF protection
- Completing OIDC discovery and JWKS endpoints
- Moving configuration to environment variables or a configuration file
- Developing a more robust user management system