{"id":34179994,"url":"https://github.com/bitrise-io/bitrise-oauth","last_synced_at":"2026-03-12T01:03:35.264Z","repository":{"id":42990002,"uuid":"299584184","full_name":"bitrise-io/bitrise-oauth","owner":"bitrise-io","description":null,"archived":false,"fork":false,"pushed_at":"2025-12-15T03:57:23.000Z","size":199,"stargazers_count":3,"open_issues_count":4,"forks_count":2,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-12-18T12:14:51.204Z","etag":null,"topics":["production-code"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitrise-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-09-29T10:35:46.000Z","updated_at":"2025-11-27T15:45:23.000Z","dependencies_parsed_at":"2025-05-09T10:32:07.148Z","dependency_job_id":"713486c2-84c9-42bc-abea-b84636fe996c","html_url":"https://github.com/bitrise-io/bitrise-oauth","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bitrise-io/bitrise-oauth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitrise-io%2Fbitrise-oauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitrise-io%2Fbitrise-oauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitrise-io%2Fbitrise-oauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitrise-io%2Fbitrise-oauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitrise-io","download_url":"https://codeload.github.com/bitrise-io/bitrise-oauth/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitrise-io%2Fbitrise-oauth/sbom","scorecard":{"id":240697,"data":{"date":"2025-08-11","repo":{"name":"github.com/bitrise-io/bitrise-oauth","commit":"c8929b7d42d88a6e41da0dfa61b6c744c08d6506"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2025-3488 / GHSA-6v2p-p543-phr9"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T06:38:25.331Z","repository_id":42990002,"created_at":"2025-08-17T06:38:25.331Z","updated_at":"2025-08-17T06:38:25.331Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30410358,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T00:40:14.898Z","status":"ssl_error","status_checked_at":"2026-03-12T00:40:08.439Z","response_time":84,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["production-code"],"created_at":"2025-12-15T13:40:53.315Z","updated_at":"2026-03-12T01:03:35.259Z","avatar_url":"https://github.com/bitrise-io.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://app.bitrise.io/app/e6a7166eda823c72/status.svg?token=LACL0_krbTkiMlmi4kBLNA\u0026branch=master)](https://app.bitrise.io/app/e6a7166eda823c72)\n\n# Bitrise OAuth library for Go\nThis package is a very thin layer over Go's standard [OAuth2 library](https://github.com/golang/oauth2) and the [Auth0 library](https://github.com/bitrise-io/auth0-go) for Go, extending their functionality via introducing an additional layer that handles the initialization and communication with our current authorization provider [Keycloak](https://github.com/keycloak/keycloak).\n\nThis package provides both *client-side* and *server-side* wrappers, covering all of our current use-cases. In this document, you may find useful information about the APIs, the custom configuration options, and the usage as well.\n\n## Client\nThe *client-side* validation logic is located in the `client` package. The package offers a convenient way to gain an access token on the *client-side*. It was achieved by extending Go's standard `http.Client`. It basically holds the necessary parameters for a successful token request (like **client ID**, **client secret**, and the **token URL** of the authorization server). You can use the `AuthProvider` in several different ways to gain an access token. You may find information about each use-case in the API paragraph. One important thing to note is that in this context *client* means service client, participating in service-to-service authentication.\n\n### API\n#### `AuthProvider` interface\nDescribes the possible operations and use-cases of our package.\n\n#### `WithSecret` impl\nImplements the `AuthProvider` interface. This class is used to gain an authenticated `http.Client` to make further authenticated *HTTP* calls, or alternatively, a token source can be created as well, but in this case, only the access token can be gained, not a complete authenticated `http.Client`. You can use `HTTPClientOption`s to configure.\n\n##### Fields\n- `clientID string` holds the client ID.\n\n- `clientSecret string` holds the client secret.\n\n- `tokenURL string` holds the URL of the authentication service that provides an access token.\n\n- `credentials clientcredentials.Config` holds the parameters above in an `oauth.clientcredentials.Config` instance, used by the underlying *OAuth* library.\n\n##### Methods\n- `NewWithSecret(clientID, clientSecret string, scopeOption ScopeOption, opts ...Option) AuthProvider` returns a new instance of `AuthProvider`. Setting the authentication scope is mandatory. Furthermore, it might receive `Option`s as a parameter.\n\n- `TokenSource() oauth2.TokenSource` returns an `oauth.clientcredentials.TokenSource` that returns the token until it expires, automatically refreshing it as necessary using the provided context and the client ID and client secret.\n\n- `UMATokenSource() UMATokenSource` returns an `UMATokenSource` that returns a new token upon everytime a new token is acquired. You might want to use it for authorization purposes.\n\n- `HTTPClient(opts ...HTTPClientOption) *http.Client` returns a preconfigured `http.Client`.\n\n- `ManagedHTTPClient(opts ...HTTPClientOption) *http.Client` returns a preconfigured `http.Client`. Uses a thread-safe map to store the created clients, using the `clientID` + `clientSecret` + `tokenURL` combination as a key. When the function is called, it will try to retrieve an existing instance from the map by the credentials. If found, the instance will be returned. Otherwise, a new instance will be created, saved in the map, and returned.\n\n#### `UMATokenSource`\nThis is responsible for providing authorization purposes. Similarly to the regular `ouath2.TokenSource`, it has a `Token()` method that returns a new token upon each invocation.\n\n##### Methods\n- `Token(payload interface{}, permisson []Permission, audienceConfig config.AudienceConfig) (*oauth2.Token, error)` returns a new token upon each invocation.\n\n#### `Permission`\nIt represents an authorization permission.\n\n\n### Options\nThe package offers wide configurability using Options. You can easily override any parameter by passing the desired Option(s) as constructor arguments. Not only the `AuthProvider` itself has Options, but each use-case has their own Options as well, offering further configuration possibilities.\n\n#### Option\n- `WithBaseURL(baseURL string) Option` overrides the base URL of the authentication service.\n\n- `WithRealm(realm string) VOption` overrides the realm.\n\n- `WithScope(aud string, auds ...string) ScopeOption` sets the authentication scope.\n\n#### HTTPClientOption\n- `WithContext(ctx context.Context) HTTPClientOption` overrides the HTTP context of the client.\n\n- `WithBaseClient(bc *http.Client) HTTPClientOption` can extend an already existing HTTP client.\n\n### Usage\n```go\nauthProvider := client.NewWithSecret(\"my-client-id\", \"my-client-secret\")\nresp, err := authProvider.ManagedHTTPClient().Get(\"https://myservice.services.bitrise.io/\")\n```\n\n## Server\nThe server-side validation logic is located in the `service` package. You can use the `Validator` in several different ways to validate any request. The supported use-cases are the following:\n- **Handler Function** with:\n\t- Go's default HTTP multiplexer\n\t- Gorilla's HTTP router called [**gorilla/mux**](https://github.com/gorilla/mux)\n- **Middleware** with:\n\t- Go's default HTTP multiplexer\n\t- Gorilla's HTTP router called [**gorilla/mux**](https://github.com/gorilla/mux)\n- **Middleware Function** and **Handler Function** with Labstack's router called [**echo**](https://github.com/labstack/echo)\n\n\n### API\n#### `ValidatorIntf`\nDescribes the possible operations and use-cases of our package.\n\n#### `Validator`\nImplements the `ValidatorIntf` interface. As its name reflects, this class is responsible for the validation of a request, using an `auth0.Validator` instance.\nYou can use `ValidatorOption`s to configure.\n\n##### Fields\n- `validator JWTValidator` holds the `auth0.JWTValidator` instance, used to validate a request. You may find further information about the `JWTValidator` interface in the next paragraph.\n\n- `baseURL string` holds the base URL of the authentication service.\n\n- `realm string` holds the realm.\n\n- `keyCacher auth0.KeyCacher` holds the *JWK* cacher. By default it can hold **5 keys** at max, for no longer than **2 hours**.\n\n- `jwksURL string` holds the keystore URL.\n\n- `realmURL string` holds the realm URL.\n\n- `signatureAlgorithm jose.SignatureAlgorithm` holds the encryption/decription algorithm of the *JWT*. By default this is `RS256`.\n\n- `timeout time.Duration` holds the timeout duration. By default this is **30 seconds**.\n\n##### Methods\n- `NewValidator(audienceConfig AudienceConfig, opts ...ValidatorOption) ValidatorIntf` returns a new instance of `Validator`. Setting the expected audience is mandatory. Furthermore, it might receive `ValidatorOption`s as a parameter.\n\n- `ValidateRequest(r *http.Request) error` calls the `ValidateRequest` function of `auth0.JWTValidator` instance to validate a request. It returns `nil` if the validation has succeeded, otherwise returns an `error`.\n\n- `ValidateRequestAndReturnToken(r *http.Request) (*TokenWithClaims, error)` calls the `ValidateRequest` function of `auth0.JWTValidator` instance to validate a request. It returns the validated `TokenWithClaims` token, that can be used to get the claims if the validation has succeeded, otherwise returns an `error`.\n\n- `Middleware(next http.Handler, opts ...HTTPMiddlewareOption) http.Handler` returns an `http.Handler` instance. It calls `ValidateRequest` to validate the request. Calls the next middleware if the validation has succeeded, otherwise sends an error using an error writer. It might receive `HTTPMiddlewareOption`s as a parameter.\n\n- `EchoMiddlewareFunc(opts ...EchoMiddlewareOption) echo.MiddlewareFunc` returns an `echo.MiddlewareFunc` instance. It calls `ValidateRequest` to validate the request. Calls the next `echo.HandlerFunc` if the validation has succeeded, otherwise returns an `error`. It might receive `EchoMiddlewareOption`s as a parameter. \n\n- `HandlerFunc(hf http.HandlerFunc, opts ...HTTPMiddlewareOption) http.HandlerFunc` returns a `http.HandlerFunc` instance. It calls `ValidateRequest` to validate the request. Calls the next handler function if the validation has succeeded, otherwise sends an error using an error writer. It might receive `HTTPMiddlewareOption`s as a parameter.\n\n#### `JWTValidator`\nSince `auth0.JWTValidator` is not an interface, it was necessary to create an interface to loosen the coupling and making it exchangeable and mockable in tests.\n\n#### `AudienceConfig`\nYou can set the expceted audience via passing an `AudienceConfig` instance as a parameter upon instantiating the validator. One or more audience have to passed.\n\n##### Fields\n- `audience []string` holds the audiences.\n\n##### Methods\n- `NewAudienceConfig(audience string, audiences ...string) AudienceConfig` returns a new `AudienceConfig`. One or more audiences have to be set.\n\n- `All() []string` returns all of the audiences.\n\n##### Usage\n```go\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience1\", \"audience2\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n```\n\n#### `TokenWithClaims`\nRepresents an UMA token that holds certain claims.\n\n##### Methods\n- `Payload() (map[string]interface{}, error)` returns the contents of the token (basically all the claims in the token)\n\n- `Permissions() ([]interface{}, error)` returns the persmissions part of the token.\n\n- `Claim(resourceName string, claim interface{}) error` returns the claim for the provided resource's name.\n\n- `ValidateScopes(scopes []string) error` check if the token has ALL the passed scopes in its scope claim\n\n```go\nerr := tokenWithClaims.ValidateScopes([]string{\"app:read\", \"missing:write\"})\nif err != nil {\n\t// scope validation failed\n}\n```\n\n### Options\nThe package offers wide configurability using Options. You can easily override any parameter by passing the desired Option(s) as constructor arguments. Not only the `Validator` itself has Options, but each use-case has their own Options as well, offering further configuration possibilities.\n\n#### ValidatorOption\nIf you want to override the default options (like the auth service url, or the realm), you can customize the `Validator` during instantiation with Options. It's as easy as passing them as constructor parameters, separated by a comma:\n```go\nservice.NewValidator(config.NewAudienceConfig(\"audience\"),\n  service.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n```\n\nThe available `ValidatorOption`s are the following:\n- `WithBaseURL(url string) ValidatorOption` overrides the base URL of the authentication service.\n\n- `WithSignatureAlgorithm(sa jose.SignatureAlgorithm) ValidatorOption` overrides the encryption/decryption algorithm of the *JWT*.\n\n- `WithRealm(realm string) ValidatorOption` overrides the realm.\n\n- `WithKeyCacher(kc auth0.KeyCacher) ValidatorOption` overrides the *JWK* cacher.\n\n- `WithTimeout(timeout time.Duration) ValidatorOption` overrides the timeout for validation networking.\n\n#### HTTPMiddlewareOption\nYou can configure the *Handler Function* and *Middleware* use-cases via passing these Options either to `Validator`'s `HandlerFunc` or `Middleware` function. The available `HTTPMiddlewareOption`s are the following:\n- `WithHTTPErrorWriter(errorWriter func(w http.ResponseWriter, r *http.Request, err error)) HTTPMiddlewareOption` overrides the error writer.\n\n#### EchoMiddlewareOption\nYou can configure the *echo* use-case via passing these Options to `Validator`'s `MiddlewareFunc` function. The available `EchoMiddlewareOption`s are the following:\n- `WithContextErrorWriter(errorWriter func(echo.Context, error) error) EchoMiddlewareOption` overrides the error writer.\n\n\n### Usage\n\n#### Validating request/token and extracting claims\n```go\nfunc someHandler(w http.ResponseWriter, r *http.Request) {\t\n\ttoken, err := validator.ValidateRequestAndReturnToken(r)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tclaims, err := token.Payload()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tclaimsResponse := fmt.Sprintf(\"%v\", claims)\n\n\tw.Write([]byte(claimsResponse))\n}\n```\n\n#### Handler Function\n```go\nhandler := func(w http.ResponseWriter, r *http.Request) {}\n\nmux := http.NewServeMux()\n\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\nmux.HandleFunc(\"/test_func\", validator.HandlerFunc(handler))\n\nlog.Fatal(http.ListenAndServe(\":8080\", mux))\n```\n\n#### Handler Function with gorilla/mux\n```go\nhandler := func(w http.ResponseWriter, r *http.Request) {}\n\nrouter := mux.NewRouter()\n\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\nrouter.HandleFunc(\"/test_func\", validator.HandlerFunc(handler)).Methods(http.MethodGet)\n\nhttp.Handle(\"/\", router)\n\nlog.Fatal(http.ListenAndServe(\":8080\", router))\n```\n\n#### Middleware\n```go\nhandler := func(w http.ResponseWriter, r *http.Request) {}\n\nmux := http.NewServeMux()\n\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\nmux.Handle(\"/test\", validator.Middleware(http.HandlerFunc(handler)))\n\nlog.Fatal(http.ListenAndServe(\":8080\", mux))\n```\n\n#### Middleware with gorilla/mux\n```go\nhandler := func(w http.ResponseWriter, r *http.Request) {}\n\nrouter := mux.NewRouter()\n\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\nrouter.Handle(\"/test\", validator.Middleware(http.HandlerFunc(handler))).Methods(http.MethodGet)\n\nhttp.Handle(\"/\", router)\n\nlog.Fatal(http.ListenAndServe(\":8080\", router))\n```\n\n#### Echo Middleware Function\n```go\nhandler := func(c echo.Context) error {\n\treturn c.String(http.StatusOK, \"Hello, World!\")\n}\n\ne := echo.New()\n\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\ne.Use(validator.MiddlewareFunc())\n\ne.GET(\"/test\", handler)\n\ne.Logger.Fatal(e.Start(\":8080\"))\n```\n\n#### Echo Handler Function\n```go\nvalidator := service.NewValidator(config.NewAudienceConfig(\"audience\"),\n\tservice.WithBaseURL(\"https://auth.services.bitrise.io\"), service.WithRealm(\"master\"))\n\nhandler := func(c echo.Context) error {\n\tif err := validator.ValidateRequest(c.Request()); err != nil {\n\t\treturn err\n\t}\n\treturn c.String(http.StatusOK, \"Hello, World!\")\n}\n\ne := echo.New()\n\ne.GET(\"/test\", handler)\n\ne.Logger.Fatal(e.Start(\":8080\"))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitrise-io%2Fbitrise-oauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitrise-io%2Fbitrise-oauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitrise-io%2Fbitrise-oauth/lists"}