### Run Fosite Example Application Source: https://github.com/ory/fosite/blob/master/README.md Instructions to download, install, and run a minimalistic Fosite example application. This provides a hands-on demonstration of Fosite's capabilities. ```bash go get github.com/ory/fosite-example cd $GOPATH/src/github.com/ory/fosite-example dep ensure go install github.com/ory/fosite-example fosite-example ``` -------------------------------- ### Complete Client Credentials Server Example with JWT Access Tokens Source: https://context7.com/ory/fosite/llms.txt A runnable example demonstrating a server that issues JWT access tokens via the client credentials grant. Includes setup for RSA keys, memory storage, and HTTP handlers. ```go package main import ( "context" "crypto/rand" "crypto/rsa" "log" "net/http" "time" "github.com/ory/fosite" "github.com/ory/fosite/compose" "github.com/ory/fosite/handler/oauth2" "github.com/ory/fosite/storage" ) func main() { key, _ := rsa.GenerateKey(rand.Reader, 2048) store := storage.NewMemoryStore() store.Clients["test-client"] = &fosite.DefaultClient{ ID: "test-client", Secret: []byte(`$2a$10$IxMdI6d.LIRZPpSfEwNoeu4rY3FhDREsxFJXikcgdRRAStxUlsuEO`), // "foobar" GrantTypes: []string{"client_credentials"}, Scopes: []string{"read", "write"}, } config := &fosite.Config{AccessTokenLifespan: time.Minute * 30} provider := compose.Compose( config, store, compose.NewOAuth2JWTStrategy( func(context.Context) (interface{}, error) { return key, nil }, compose.NewOAuth2HMACStrategy(config), config, ), compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory, ) http.HandleFunc("/token", func(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() session := &oauth2.JWTSession{} ar, err := provider.NewAccessRequest(ctx, r, session) if err != nil { provider.WriteAccessError(ctx, rw, ar, err) return } resp, err := provider.NewAccessResponse(ctx, ar) if err != nil { provider.WriteAccessError(ctx, rw, ar, err) return } provider.WriteAccessResponse(ctx, rw, ar, resp) }) log.Println("Listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } // $ go run main.go // $ curl -s http://localhost:8080/token \ // -d grant_type=client_credentials \ // -d client_id=test-client \ // -d client_secret=foobar \ // -d scope=read // // {"access_token":"","expires_in":1799,"scope":"read","token_type":"bearer"} ``` -------------------------------- ### Clone Repository and Set Up Fork Source: https://github.com/ory/fosite/blob/master/CONTRIBUTING.md Steps to clone the Ory Fosite repository and set up a remote for your personal fork. This is the initial setup for contributing. ```bash git clone git@github.com:ory/ory/fosite.git ``` ```bash git remote add fork git@github.com:/ory/fosite.git ``` ```bash git fetch origin git checkout master git pull --rebase ``` ```bash git checkout my-feature-branch ``` ```bash git add -A git commit -a -m "fix: this is the subject line" -m "This is the body line. Closes #123" ``` ```bash git push -u fork my-feature-branch ``` -------------------------------- ### Contribute to Fosite Source: https://github.com/ory/fosite/blob/master/README.md Steps to set up your development environment for contributing to Fosite. Ensure you have git and golang installed. Run tests frequently to catch issues early. ```bash go get -d github.com/ory/fosite cd $GOPATH/src/github.com/ory/fosite git status git remote add myfork go test ./... ``` -------------------------------- ### Install Ory Fosite with Go Source: https://github.com/ory/fosite/blob/master/README.md Installs the Ory Fosite library and its dependencies using the Go package manager. Ensure Go 1.11+ and GOPATH are set up. ```bash go get -u github.com/ory/fosite/... ``` -------------------------------- ### Running the Server and Issuing a Token Source: https://github.com/ory/fosite/blob/master/docs/how-tos/client_credentials_grant.md These commands show how to run the Fosite server and then use curl to request an access token using the Client Credentials Grant. The example uses a predefined client ID and secret. ```bash $go run . 2021/04/26 12:57:24 serving on 0.0.0.0:8080 ``` ```bash $curl http://localhost:8080/token -d grant_type=client_credentials -d client_id=test-client -d client_secret=foobar { "access_token": "", "expires_in": 1799, "scope": "", "token_type": "bearer" } ``` -------------------------------- ### Fosite Provider Setup for Client Credentials Grant Source: https://github.com/ory/fosite/blob/master/docs/how-tos/client_credentials_grant.md This Go code sets up a Fosite OAuth2 provider with a JWT strategy for signing access tokens and configures it for the Client Credentials Grant. It includes generating an RSA key, setting up an in-memory store, and registering a client. ```go package main import ( "crypto/rand" "crypto/rsa" "log" "net/http" "time" "github.com/ory/fosite" "github.com/ory/fosite/compose" "github.com/ory/fosite/storage" ) func main() { // Generates a RSA key to sign JWT tokens key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { log.Fatalf("Cannot generate RSA key: %v", err) } var storage = storage.NewMemoryStore() // Register a test client in the memory store storage.Clients["test-client"] = &fosite.DefaultClient{ ID: "test-client", Secret: []byte(`$2a$10$IxMdI6d.LIRZPpSfEwNoeu4rY3FhDREsxFJXikcgdRRAStxUlsuEO`), // = "foobar" GrantTypes: []string{"client_credentials"}, } // check the api docs of compose.Config for further configuration options var config = &compose.Config{ AccessTokenLifespan: time.Minute * 30, } var oauth2Provider = compose.Compose( config, storage, compose.NewOAuth2JWTStrategy( key, // HMACStrategy is used to sign refresh token // therefore not required for our example nil, ), // BCrypt hasher is automatically created when omitted. // Hasher is used to store hashed client authentication passwords. nil, compose.OAuth2ClientCredentialsGrantFactory, ) accessTokenHandler := tokenHandler{oauth: oauth2Provider} http.HandleFunc("/token", accessTokenHandler.TokenHandler) log.Println("serving on 0.0.0.0:8080") if err := http.ListenAndServe("0.0.0.0:8080", nil); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Configure Fosite Scope Strategy Source: https://github.com/ory/fosite/blob/master/README.md Sets the scope strategy for Fosite using the composer. The HierarchicScopeStrategy is used as an example. To issue refresh tokens, include the 'offline' scope. ```go import "github.com/ory/fosite" var config = &fosite.Config{ ScopeStrategy: fosite.HierarchicScopeStrategy, } ``` -------------------------------- ### Client Registration Source: https://context7.com/ory/fosite/llms.txt Details on registering OAuth2 clients using the `DefaultClient` struct or by implementing the `Client` interface. Includes examples for confidential, public, and OpenID Connect clients. ```APIDOC ## `DefaultClient` / `Client` Interface — Registering OAuth2 Clients Fosite requires a `Storage` implementation that returns `Client` objects. Use `DefaultClient` for simple cases or implement the `Client` interface for custom logic. ### Example Storage Setup ```go import ( "github.com/ory/fosite" "github.com/ory/fosite/storage" ) // In-memory store for testing / example purposes store := storage.NewMemoryStore() ``` ### Registering a Confidential Client ```go // Register a confidential client (authorization code + client credentials) store.Clients["web-app"] = &fosite.DefaultClient{ ID: "web-app", Secret: []byte(`$2a$10$IxMdI6d.LIRZPpSfEwNoeu4rY3FhDREsxFJXikcgdRRAStxUlsuEO`), // bcrypt of "foobar" RedirectURIs: []string{"https://myapp.example.com/callback"}, GrantTypes: []string{"authorization_code", "refresh_token"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "profile", "email", "offline"}, Audience: []string{"https://api.example.com"}, Public: false, } ``` ### Registering a Public Client ```go // Register a public client (SPA / mobile — no secret, PKCE required) store.Clients["spa"] = &fosite.DefaultClient{ ID: "spa", RedirectURIs: []string{"https://spa.example.com/callback"}, GrantTypes: []string{"authorization_code"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "profile"}, Public: true, // no client secret } ``` ### Registering an OpenID Connect Client with JWKS ```go // Register an OpenID Connect client with JWKS store.Clients["oidc-client"] = &fosite.DefaultOpenIDConnectClient{ DefaultClient: &fosite.DefaultClient{ ID: "oidc-client", RedirectURIs: []string{"https://rp.example.com/callback"}, GrantTypes: []string{"authorization_code"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "email"}, }, JSONWebKeysURI: "https://rp.example.com/.well-known/jwks.json", TokenEndpointAuthMethod: "private_key_jwt", TokenEndpointAuthSigningAlgorithm: "RS256", } ``` ``` -------------------------------- ### Build `OAuth2Provider` with Selective Handlers using `compose.Compose` Source: https://context7.com/ory/fosite/llms.txt Use `compose.Compose` to build an `OAuth2Provider` with only the specified grant-type factories. This example registers a JWT-based client credentials flow with introspection and revocation, using an HMAC strategy as a fallback for refresh tokens. Ensure `privateKey` is defined elsewhere. ```go import ( "github.com/ory/fosite" "github.com/ory/fosite/compose" "github.com/ory/fosite/storage" ) config := &fosite.Config{ AccessTokenLifespan: time.Minute * 30, GlobalSecret: []byte("my-32-byte-long-super-secret!!1"), } store := storage.NewMemoryStore() // Register only a JWT-based client credentials flow + introspection provider := compose.Compose( config, store, compose.NewOAuth2JWTStrategy( func(ctx context.Context) (interface{}, error) { return privateKey, nil }, compose.NewOAuth2HMACStrategy(config), // fallback HMAC for refresh tokens config, ), compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory, compose.OAuth2TokenRevocationFactory, ) ``` -------------------------------- ### Persist Refresh Token Grant Session with Access Token Revocation Source: https://github.com/ory/fosite/blob/master/HISTORY.md Example implementation for PersistRefreshTokenGrantSession that revokes existing access tokens before persisting a new refresh session. This enhances security by ensuring old access tokens are invalidated. ```go func (s *MemoryStore) PersistRefreshTokenGrantSession(ctx context.Context, originalRefreshSignature, accessSignature, refreshSignature string, request fosite.Requester) error { if ts, err := s.GetRefreshTokenSession(ctx, originalRefreshSignature, nil); err != nil { return err } else if err := s.RevokeAccessToken(ctx, ts.GetID()); err != nil { return err } else if err := s.RevokeRefreshToken(ctx, ts.GetID()); err != nil { return err } else if err := s.CreateAccessTokenSession(ctx, accessSignature, request); err != nil { return err } else if err := s.CreateRefreshTokenSession(ctx, refreshSignature, request); err != nil { return err } return nil } ``` -------------------------------- ### Instantiate Fosite with ComposeAllEnabled Source: https://github.com/ory/fosite/blob/master/README.md This snippet shows how to set up a complete Fosite provider with all OAuth2 and OpenID Connect handlers enabled. It requires a configuration object, a storage implementation, and an RSA private key. The `compose.ComposeAllEnabled` function is a convenience helper for this purpose. ```go package main import "github.com/ory/fosite" import "github.com/ory/fosite/compose" import "github.com/ory/fosite/storage" // This is the example storage that contains: // * an OAuth2 Client with id "my-client" and secrets "foobar" and "foobaz" capable of all oauth2 and open id connect grant and response types. // * a User for the resource owner password credentials grant type with username "peter" and password "secret". // // You will most likely replace this with your own logic once you set up a real world application. var storage = storage.NewExampleStore() // This secret is being used to sign access and refresh tokens as well as // authorization codes. It must be exactly 32 bytes long. var secret = []byte("my super secret signing password") privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { panic("unable to create private key") } // check the api docs of fosite.Config for further configuration options var config = &fosite.Config{ AccessTokenLifespan: time.Minute * 30, GlobalSecret: secret, // ... } var oauth2Provider = compose.ComposeAllEnabled(config, storage, privateKey) ``` -------------------------------- ### Configure Token Signing Strategies (HMAC vs JWT) Source: https://context7.com/ory/fosite/llms.txt Shows how to set up HMAC (opaque, stateful) and JWT (signed, stateless) strategies for Fosite. HMAC is suitable for general use, while JWTs offer stateless verification. ```go import ( "github.com/ory/fosite/compose" ) // --- HMAC Strategy (opaque tokens, stateful) --- hmacStrategy := compose.NewOAuth2HMACStrategy(config) provider := compose.Compose(config, store, hmacStrategy, compose.OAuth2AuthorizeExplicitFactory, compose.OAuth2RefreshTokenGrantFactory, ) ``` ```go // --- JWT Strategy (signed JWTs, stateless-verifiable) --- keyGetter := func(ctx context.Context) (interface{}, error) { return privateKey, nil // *rsa.PrivateKey or *ecdsa.PrivateKey } jwtStrategy := compose.NewOAuth2JWTStrategy(keyGetter, hmacStrategy, config) // hmacStrategy is still used for refresh tokens; only access tokens become JWTs providerJWT := compose.Compose(config, store, jwtStrategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory, ) ``` ```go // Stateless JWT introspection (no storage lookup — revocation NOT supported): providerStateless := compose.Compose(config, store, jwtStrategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2StatelessJWTIntrospectionFactory, // skips DB on introspect ) ``` -------------------------------- ### Configure Fosite Provider Settings Source: https://context7.com/ory/fosite/llms.txt Set token lifespans, signing secrets, scope strategies, PKCE settings, and other provider-specific configurations. Ensure secrets are of the correct length. ```go config := &fosite.Config{ // Token lifespans AccessTokenLifespan: time.Minute * 30, // default: 1 hour RefreshTokenLifespan: time.Hour * 24 * 30, // default: 30 days; -1 = never expire AuthorizeCodeLifespan: time.Minute * 15, // default: 15 minutes IDTokenLifespan: time.Hour, // default: 1 hour // Signing secret for HMAC tokens (must be 32 bytes) GlobalSecret: []byte("my-32-byte-long-super-secret!!1"), RotatedGlobalSecrets: [][]byte{[]byte("old-secret-for-verification-only")}, // Scope strategy: WildcardScopeStrategy (default), ExactScopeStrategy, HierarchicScopeStrategy ScopeStrategy: fosite.WildcardScopeStrategy, // PKCE settings EnforcePKCE: false, // require PKCE for all clients EnforcePKCEForPublicClients: true, // require PKCE for public clients only EnablePKCEPlainChallengeMethod: false, // allow plain (discouraged); default: false // OpenID Connect IDTokenIssuer: "https://auth.example.com", AllowedPromptValues: []string{"login", "none", "consent", "select_account"}, // JWT access tokens AccessTokenIssuer: "https://auth.example.com", // Pushed Authorization Requests IsPushedAuthorizeEnforced: false, PushedAuthorizeContextLifespan: time.Minute * 5, // Device Authorization Grant (RFC 8628) DeviceVerificationURL: "https://auth.example.com/device", DeviceAndUserCodeLifespan: time.Minute * 10, DeviceAuthTokenPollingInterval: time.Second * 5, // Debug (never enable in production) SendDebugMessagesToClients: false, } ``` -------------------------------- ### Register OAuth2 Clients with Fosite Storage Source: https://context7.com/ory/fosite/llms.txt Demonstrates registering OAuth2 clients using `fosite.DefaultClient` and `fosite.MemoryStore`. Shows configurations for confidential, public, and OpenID Connect clients with different grant types, response types, and scopes. ```go import ( "github.com/ory/fosite" "github.com/ory/fosite/storage" ) // In-memory store for testing / example purposes store := storage.NewMemoryStore() // Register a confidential client (authorization code + client credentials) store.Clients["web-app"] = &fosite.DefaultClient{ ID: "web-app", Secret: []byte(`$2a$10$IxMdI6d.LIRZPpSfEwNoeu4rY3FhDREsxFJXikcgdRRAStxUlsuEO`), // bcrypt of "foobar" RedirectURIs: []string{"https://myapp.example.com/callback"}, GrantTypes: []string{"authorization_code", "refresh_token"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "profile", "email", "offline"}, Audience: []string{"https://api.example.com"}, Public: false, } // Register a public client (SPA / mobile — no secret, PKCE required) store.Clients["spa"] = &fosite.DefaultClient{ ID: "spa", RedirectURIs: []string{"https://spa.example.com/callback"}, GrantTypes: []string{"authorization_code"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "profile"}, Public: true, // no client secret } // Register an OpenID Connect client with JWKS store.Clients["oidc-client"] = &fosite.OpenIDConnectClient{ DefaultClient: &fosite.DefaultClient{ ID: "oidc-client", RedirectURIs: []string{"https://rp.example.com/callback"}, GrantTypes: []string{"authorization_code"}, ResponseTypes: []string{"code"}, Scopes: []string{"openid", "email"}, }, JSONWebKeysURI: "https://rp.example.com/.well-known/jwks.json", TokenEndpointAuthMethod: "private_key_jwt", TokenEndpointAuthSigningAlgorithm: "RS256", } ``` -------------------------------- ### Configure Scope Strategies in Fosite Source: https://context7.com/ory/fosite/llms.txt Demonstrates how to configure different scope strategies for Fosite. Choose the strategy that best fits your application's scope matching requirements. ```go // WildcardScopeStrategy (default): glob-style matching // "users.*" matches "users.read", "users.write.own" // "users" does NOT match "users.read" config := &fosite.Config{ ScopeStrategy: fosite.WildcardScopeStrategy, } ``` ```go // ExactScopeStrategy: token-for-token equality config2 := &fosite.Config{ ScopeStrategy: fosite.ExactScopeStrategy, } ``` ```go // HierarchicScopeStrategy (deprecated): prefix matching // "users" matches "users.read", "users.write.own" // "users.read" does NOT match "users.write" config3 := &fosite.Config{ ScopeStrategy: fosite.HierarchicScopeStrategy, } ``` ```go // Custom scope strategy — implement the ScopeStrategy function type: // type ScopeStrategy func(haystack []string, needle string) bool customStrategy := fosite.ScopeStrategy(func(haystack []string, needle string) bool { for _, s := range haystack { if s == needle || s == "*" { return true } } return false }) config4 := &fosite.Config{ScopeStrategy: customStrategy} ``` -------------------------------- ### Instantiate Full-Featured OAuth2 + OIDC Provider with `compose.ComposeAllEnabled` Source: https://context7.com/ory/fosite/llms.txt Use `compose.ComposeAllEnabled` to create an `OAuth2Provider` with all standard OAuth2 and OpenID Connect handlers enabled. This requires a configuration, a storage backend, and an RSA private key for signing tokens. Replace `storage.NewExampleStore()` with your custom storage implementation. ```go package main import ( "crypto/rand" "crypto/rsa" "net/http" "time" "github.com/ory/fosite" "github.com/ory/fosite/compose" "github.com/ory/fosite/storage" ) func main() { // RSA private key used to sign OIDC ID tokens and JWT access tokens privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { panic(err) } store := storage.NewExampleStore() // replace with your own storage config := &fosite.Config{ AccessTokenLifespan: time.Minute * 30, RefreshTokenLifespan: time.Hour * 24 * 7, AuthorizeCodeLifespan: time.Minute * 10, IDTokenLifespan: time.Hour, GlobalSecret: []byte("my-32-byte-long-super-secret!!1"), // exactly 32 bytes ScopeStrategy: fosite.WildcardScopeStrategy, EnforcePKCEForPublicClients: true, IDTokenIssuer: "https://myapp.example.com", } // All handlers registered in one call provider := compose.ComposeAllEnabled(config, store, privateKey) http.HandleFunc("/oauth2/auth", authorizeHandler(provider)) http.HandleFunc("/oauth2/token", tokenHandler(provider)) http.HandleFunc("/oauth2/revoke", revokeHandler(provider)) http.ListenAndServe(":3846", nil) } ``` -------------------------------- ### Run Project Tests Source: https://github.com/ory/fosite/blob/master/CONTRIBUTING.md Execute the full project test suite. Ensure all tests pass before submitting changes. ```bash go test -tags sqlite ./... ``` -------------------------------- ### Format Code Source: https://github.com/ory/fosite/blob/master/CONTRIBUTING.md Format all source code according to Ory standards. This command should be run before committing changes. ```bash make format ``` -------------------------------- ### Handle OAuth2 Access Request Source: https://github.com/ory/fosite/blob/master/README.md This snippet demonstrates how to create a new access request and generate a response. It includes error handling and conditional logic based on session data. Ensure the oauth2Provider is properly initialized. ```go accessRequest, err := oauth2Provider.NewAccessRequest(ctx, req, mySessionData) if err != nil { oauth2Provider.WriteAccessError(ctx, rw, accessRequest, err) return } if mySessionData.Username == "super-admin-guy" { // do something... } // Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers // and aggregate the result in response. response, err := oauth2Provider.NewAccessResponse(ctx, accessRequest) if err != nil { oauth2Provider.WriteAccessError(ctx, rw, accessRequest, err) return } // All done, send the response. oauth2Provider.WriteAccessResponse(ctx, rw, accessRequest, response) // The client has a valid access token now } ``` -------------------------------- ### PKCERequestStorage Interface Definition Source: https://github.com/ory/fosite/blob/master/HISTORY.md Implement these methods if you use the PKCE handler to manage PKCE request sessions. ```go type PKCERequestStorage interface { GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error DeletePKCERequestSession(ctx context.Context, signature string) error } ``` -------------------------------- ### Add Audience Methods to fosite.Requester Interface Source: https://github.com/ory/fosite/blob/master/HISTORY.md Introduces methods for managing requested and granted audiences within the fosite.Requester interface. ```go type fosite.Requester interface { + // GetRequestedAudience returns the requested audiences for this request. + GetRequestedAudience() (audience Arguments) + // SetRequestedAudience sets the requested audienc. + SetRequestedAudience(audience Arguments) + // GetGrantedAudience returns all granted scopes. + GetGrantedAudience() (grantedAudience Arguments) + // GrantAudience marks a request's audience as granted. + GrantAudience(audience string) } ``` -------------------------------- ### Generate Mock Objects Source: https://github.com/ory/fosite/blob/master/README.md Command to generate mock objects for Fosite. This can be done by running the script directly or executing its contents in a shell. ```bash ./generate-mocks.sh ``` -------------------------------- ### Add Context Parameter to ClientManager.GetClient Source: https://github.com/ory/fosite/blob/master/HISTORY.md The `ClientManager.GetClient` method now requires a `context.Context` parameter. This change aligns with standard Go practices for context propagation. ```go type ClientManager interface { // GetClient loads the client by its ID or returns an error // if the client does not exist or another error occurred. - GetClient(id string) (Client, error) + GetClient(ctx context.Context, id string) (Client, error) } ``` -------------------------------- ### Fosite Authorize Endpoint Handler Source: https://github.com/ory/fosite/blob/master/README.md This function handles the OAuth2 authorization requests. It creates an `AuthorizeRequest`, checks for user authentication (simulated here), creates a session, and generates an authorization response. It includes error handling and redirects the user back to the client with an authorization code. ```go // The authorize endpoint is usually at "https://mydomain.com/oauth2/auth". func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) { // This context will be passed to all methods. It doesn't fulfill a real purpose in the standard library but could be used // to abort database lookups or similar things. ctx := req.Context() // Let's create an AuthorizeRequest object! // It will analyze the request and extract important information like scopes, response type and others. ar, err := oauth2Provider.NewAuthorizeRequest(ctx, req) if err != nil { oauth2Provider.WriteAuthorizeError(ctx, rw, ar, err) return } // Normally, this would be the place where you would check if the user is logged in and gives his consent. // We're simplifying things and just checking if the request includes a valid username and password if req.Form.Get("username") != "peter" { rw.Header().Set("Content-Type", "text/html;charset=UTF-8") rw.Write([]byte(`

Login page

`)) rw.Write([]byte(`

Howdy! This is the log in page. For this example, it is enough to supply the username.

try peter
`)) return } // Now that the user is authorized, we set up a session. When validating / looking up tokens, we additionally get // the session. You can store anything you want in it. // The session will be persisted by the store and made available when e.g. validating tokens or handling token endpoint requests. // The default OAuth2 and OpenID Connect handlers require the session to implement a few methods. Apart from that, the // session struct can be anything you want it to be. mySessionData := &fosite.DefaultSession{ Username: req.Form.Get("username"), } // It's also wise to check the requested scopes, e.g.: // if authorizeRequest.GetScopes().Has("admin") { // http.Error(rw, "you're not allowed to do that", http.StatusForbidden) // return // } // Now we need to get a response. This is the place where the AuthorizeEndpointHandlers kick in and start processing the request. // NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library // to support open id connect. response, err := oauth2Provider.NewAuthorizeResponse(ctx, ar, mySessionData) if err != nil { oauth2Provider.WriteAuthorizeError(ctx, rw, ar, err) return } // Awesome, now we redirect back to the client redirect uri and pass along an authorize code oauth2Provider.WriteAuthorizeResponse(ctx, rw, ar, response) } ``` -------------------------------- ### Update Compose Function Signature with Hasher Source: https://github.com/ory/fosite/blob/master/HISTORY.md The `compose.Compose` function signature has been updated to include an optional `fosite.Hasher` parameter. Pass nil to use the default hasher. ```go package compose -func Compose(config *Config, storage interface{}, strategy interface{}, factories ...Factory) fosite.OAuth2Provider { +func Compose(config *Config, storage interface{}, strategy interface{}, hasher fosite.Hasher, factories ...Factory) fosite.OAuth2Provider { ``` -------------------------------- ### Set HierarchicScopeStrategy in Fosite Composer Source: https://github.com/ory/fosite/blob/master/HISTORY.md Configure the Fosite composer to use the HierarchicScopeStrategy by default. This is useful when the default WildcardScopeStrategy is not desired. ```go import "github.com/ory/fosite/compose" var config = &compose.Config{ ScopeStrategy: fosite.HierarchicScopeStrategy, } ``` -------------------------------- ### Handle Device Authorization Grant Source: https://context7.com/ory/fosite/llms.txt Implement the handler for the Device Authorization endpoint. This function processes requests, generates a `device_code` and `user_code`, and provides a `verification_uri` for user approval on a secondary device. ```go func deviceAuthHandler(provider fosite.OAuth2Provider) http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() dr, err := provider.NewDeviceRequest(ctx, req) if err != nil { // write 400 error return } session := new(fosite.DefaultSession) resp, err := provider.NewDeviceResponse(ctx, dr, session) if err != nil { return } provider.WriteDeviceResponse(ctx, rw, dr, resp) // Response: // { // "device_code": "", // "user_code": "ABCD-EFGH", // "verification_uri": "https://auth.example.com/device", // "verification_uri_complete": "https://auth.example.com/device?user_code=ABCD-EFGH", // "expires_in": 600, // "interval": 5 // } } } // The device then polls the token endpoint: // curl -X POST http://localhost:3846/oauth2/token \ // -d grant_type=urn:ietf:params:oauth:grant-type:device_code \ // -d device_code= \ // -d client_id=my-client ``` -------------------------------- ### Add Context to JWTStrategy Methods Source: https://github.com/ory/fosite/blob/master/HISTORY.md Enhances the JWTStrategy interface by adding context.Context parameters to Generate, Validate, GetSignature, Hash, and Decode methods. ```go type JWTStrategy interface { - Generate(claims jwt.Claims, header Mapper) (string, string, error) + Generate(ctx context.Context, claims jwt.Claims, header Mapper) (string, string, error) - Validate(token string) (string, error) + Validate(ctx context.Context, token string) (string, error) - GetSignature(token string) (string, error) + GetSignature(ctx context.Context, token string) (string, error) - Hash(in []byte) ([]byte, error) + Hash(ctx context.Context, in []byte) ([]byte, error) - Decode(token string) (*jwt.Token, error) + Decode(ctx context.Context, token string) (*jwt.Token, error) GetSigningMethodLength() int } ``` -------------------------------- ### Access Token Request Source: https://github.com/ory/fosite/wiki/OAuth2-API-Key-Grant-Type-Draft This snippet shows how to request an access token using the API Key grant type. It includes the necessary parameters like `grant_type`, `access_token`, `scope`, and optional parameters for specifying allowed URLs and namespaces. ```APIDOC ## POST /token ### Description Requests an access token using the API Key grant type. ### Method POST ### Endpoint /token ### Parameters #### Request Body - **grant_type** (string) - Required - Must be set to `api_key`. - **access_token** (string) - Required - The API Key to be used. - **scope** (string) - Optional - A space-separated list of scopes. - **allowed_urls** (string) - Optional - A whitelist of allowed URLs. - **allowed_ios_namespaces** (string) - Optional - A whitelist of allowed iOS namespaces. - **allowed_android_namespaces** (string) - Optional - A whitelist of allowed Android namespaces. ### Request Example ```http POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=api_key access_token=AIzaSyDpoRrKVcfWXvUAvUrJ3k2JYLT-4PatY5s scope=foo bar baz allowed_urls=*.host.com/* www.host.com/path/* www.host.com/with%20spaces allowed_ios_namespaces=com.example.MyApp allowed_android_namespaces= ``` ``` -------------------------------- ### Handle OAuth2 Authorization Endpoint Source: https://context7.com/ory/fosite/llms.txt Processes incoming authorization requests, checks user authentication, grants scopes, creates a session, and generates the appropriate response (code or token). ```go func authorizeHandler(provider fosite.OAuth2Provider) http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() // 1. Parse and validate the incoming authorization request (client, scopes, redirect_uri, state, PKCE, etc.) ar, err := provider.NewAuthorizeRequest(ctx, req) if err != nil { provider.WriteAuthorizeError(ctx, rw, ar, err) return } // 2. Check user authentication (example: form-based login gate) userID := req.PostFormValue("user_id") if userID == "" { rw.Header().Set("Content-Type", "text/html;charset=UTF-8") rw.Write([]byte(`
`)) return } // 3. Optionally check requested scopes before granting for _, scope := range ar.GetRequestedScopes() { ar.GrantScope(scope) // grant all requested scopes; add ACL logic here } for _, aud := range ar.GetRequestedAudience() { ar.GrantAudience(aud) } // 4. Create a session (stored alongside the authorization code / token) session := &fosite.DefaultSession{ Username: userID, Subject: userID, } // 5. Generate the response (authorization code, token, id_token, …) response, err := provider.NewAuthorizeResponse(ctx, ar, session) if err != nil { provider.WriteAuthorizeError(ctx, rw, ar, err) return } // 6. Redirect back to client with code or token provider.WriteAuthorizeResponse(ctx, rw, ar, response) } } ``` -------------------------------- ### Handle Pushed Authorization Request Source: https://context7.com/ory/fosite/llms.txt Implement the handler for the Pushed Authorization Request (PAR) endpoint. This function processes POST requests, creates a PAR request, and generates a `request_uri` for subsequent use at the authorize endpoint. ```go func parHandler(provider fosite.OAuth2Provider) http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() ar, err := provider.NewPushedAuthorizeRequest(ctx, req) if err != nil { provider.WritePushedAuthorizeError(ctx, rw, ar, err) return } session := new(fosite.DefaultSession) resp, err := provider.NewPushedAuthorizeResponse(ctx, ar, session) if err != nil { provider.WritePushedAuthorizeError(ctx, rw, ar, err) return } provider.WritePushedAuthorizeResponse(ctx, rw, ar, resp) // Response: {"request_uri":"urn:ietf:params:oauth:request_uri:","expires_in":300} } } // Then use at authorize endpoint: // GET /oauth2/auth?client_id=my-client&request_uri=urn:ietf:params:oauth:request_uri: ``` -------------------------------- ### Add Context to Hasher Interface Methods Source: https://github.com/ory/fosite/blob/master/HISTORY.md Incorporates context.Context parameters into the Compare and Hash methods of the Hasher interface for asynchronous operations. ```go type Hasher interface { - Compare(hash, data []byte) error + Compare(ctx context.Context, hash, data []byte) error - Hash(data []byte) ([]byte, error) + Hash(ctx context.Context, data []byte) ([]byte, error) } ``` -------------------------------- ### Add GetAudience to fosite.Client Interface Source: https://github.com/ory/fosite/blob/master/HISTORY.md This change adds the GetAudience method to the fosite.Client interface to retrieve allowed audiences for a client. ```go type fosite.Client interface { + // GetAudience returns the allowed audience(s) for this client. + GetAudience() Arguments } ``` -------------------------------- ### Fosite Token Endpoint Handler Source: https://github.com/ory/fosite/blob/master/README.md This function handles OAuth2 token requests. It initializes an empty session object, which can be used by the storage implementation to unmarshal session data. It then creates an access request object and validates it against the registered `TokenEndpointHandlers`. ```go // The token endpoint is usually at "https://mydomain.com/oauth2/token" func tokenHandlerFunc(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() // Create an empty session object that will be passed to storage implementation to populate (unmarshal) the session into. // By passing an empty session object as a "prototype" to the store, the store can use the underlying type to unmarshal the value into it. // For an example of storage implementation that takes advantage of that, see SQL Store (fosite_store_sql.go) from ory/Hydra project. mySessionData := new(fosite.DefaultSession) // This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request. ``` -------------------------------- ### Token Endpoint Handler for Client Credentials Grant Source: https://github.com/ory/fosite/blob/master/docs/how-tos/client_credentials_grant.md This Go code implements the HTTP handler for the token endpoint, processing access token requests using the Client Credentials Grant. It creates and validates access token requests and generates JWT access tokens. ```go package main import ( "net/http" "github.com/ory/fosite" "github.com/ory/fosite/handler/oauth2" ) type tokenHandler struct { oauth fosite.OAuth2Provider } func (t *tokenHandler) TokenHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // A JWT session allows to configure JWT // header, body and claims for the *access token*. // Sessions also keeps data between calls in a flow // but the client credentials flow only uses the Token Endpoint ssession := &oauth2.JWTSession{} // NewAccessRequest creates an [Access Token Request](https://tools.ietf.org/html/rfc6749#section-4.1.3) // if the given http request is valid. ar, err := t.oauth.NewAccessRequest(ctx, r, session) if err != nil { t.oauth.WriteAccessError(w, ar, err) return } // NewAccessResponse creates a [Access Token Response](https://tools.ietf.org/html/rfc6749#section-4.1.4) // from a *Access Token Request*. // This response has methods and attributes to setup a valid RFC response // for Token Endpont, for example: // // ``` // { // "access_token":"2YotnFZFEjr1zCsicMWpAA", // "token_type":"example", // "expires_in":3600, // "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", // "example_parameter":"example_value" // } // ``` response, err := t.oauth.NewAccessResponse(ctx, ar) if err != nil { t.oauth.WriteAccessError(w, ar, err) return } // WriteAccessResponse writes the Access Token Response // as a HTTP response t.oauth.WriteAccessResponse(w, ar, response) } ``` -------------------------------- ### Access Token Response Source: https://github.com/ory/fosite/wiki/OAuth2-API-Key-Grant-Type-Draft This snippet illustrates a successful response when an access token is issued using the API Key grant type. It contains the `access_token` and its `token_type`. ```APIDOC ## Access Token Response (200 OK) ### Description Successful response containing the issued access token. ### Response #### Success Response (200) - **access_token** (string) - The issued API key access token. - **token_type** (string) - The type of the token, which is `api_key`. ### Response Example ```json { "access_token":"AIzaSyDpoRrKVcfWXvUAvUrJ3k2JYLT-4PatY5s", "token_type":"api_key" } ``` ``` -------------------------------- ### Add Context to OpenIDConnectRequestValidator ValidatePrompt Method Source: https://github.com/ory/fosite/blob/master/HISTORY.md Updates the ValidatePrompt method signature in OpenIDConnectRequestValidator to include a context.Context parameter. ```go - func (v *OpenIDConnectRequestValidator) ValidatePrompt(req fosite.AuthorizeRequester) error { + func (v *OpenIDConnectRequestValidator) ValidatePrompt(ctx context.Context, req fosite.AuthorizeRequester) error { } ``` -------------------------------- ### Handle Token Revocation Requests Source: https://context7.com/ory/fosite/llms.txt Implements RFC 7009 token revocation. This handler accepts both access tokens and refresh tokens for revocation. ```go func revokeHandler(provider fosite.OAuth2Provider) http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() err := provider.NewRevocationRequest(ctx, req) provider.WriteRevocationResponse(ctx, rw, err) // On success: 200 OK with empty body // On error: 400 Bad Request with RFC 6749 error JSON } } ``` ```bash curl -X POST http://localhost:3846/oauth2/revoke \ -u my-client:foobar \ -d token= ``` -------------------------------- ### Token Endpoint Source: https://context7.com/ory/fosite/llms.txt Handles the `/oauth2/token` endpoint for all grant types. It parses requests, authenticates clients, dispatches to grant handlers, and generates access, refresh, and ID tokens. ```APIDOC ## POST /oauth2/token ### Description Handles the `/oauth2/token` endpoint for all grant types (authorization code, client credentials, refresh token, device code, JWT bearer, ROPC). ### Method POST ### Endpoint `/oauth2/token` ### Request Body - **grant_type** (string) - Required - The grant type being used (e.g., `authorization_code`, `client_credentials`). - **client_id** (string) - Required - The client ID. - **client_secret** (string) - Required - The client secret. - **scope** (string) - Optional - The requested scopes. ### Request Example ```bash curl -X POST http://localhost:3846/oauth2/token \ -d grant_type=client_credentials \ -d client_id=my-client \ -d client_secret=foobar \ -d scope=read ``` ### Response #### Success Response (200) - **access_token** (string) - The generated access token. - **token_type** (string) - The type of token (e.g., `bearer`). - **expires_in** (integer) - The lifetime in seconds of the access token. - **refresh_token** (string) - Optional - The generated refresh token. - **id_token** (string) - Optional - The generated ID token. #### Response Example ```json { "access_token": "", "expires_in": 1799, "scope": "read", "token_type": "bearer" } ``` ```