...

Text file src/github.com/ory/fosite/README.md

Documentation: github.com/ory/fosite

     1<h1 align="center"><img src="./docs/image/banner_fosite.png" alt="ORY Fosite - Security-first OAuth2 framework"></h1>
     2
     3[![Build Status](https://travis-ci.org/ory/fosite.svg?branch=master)](https://travis-ci.org/ory/fosite?branch=master)
     4[![Coverage Status](https://coveralls.io/repos/ory/fosite/badge.svg?branch=master&service=github&foo)](https://coveralls.io/github/ory/fosite?branch=master)
     5[![Go Report Card](https://goreportcard.com/badge/ory/fosite)](https://goreportcard.com/report/ory/fosite)
     6
     7[![Join the chat at https://www.ory.sh/chat](https://img.shields.io/badge/join-chat-00cc99.svg)](https://www.ory.sh/chat)
     8
     9**The security first OAuth2 & OpenID Connect framework for
    10[Go](https://golang.org).** Built simple, powerful and extensible. This library
    11implements peer-reviewed [IETF RFC6749](https://tools.ietf.org/html/rfc6749),
    12counterfeits weaknesses covered in peer-reviewed
    13[IETF RFC6819](https://tools.ietf.org/html/rfc6819) and countermeasures various
    14database attack scenarios, keeping your application safe when that hacker
    15penetrates or leaks your database. OpenID Connect is implemented according to
    16[OpenID Connect Core 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-core-1_0.html)
    17and includes all flows: code, implicit, hybrid.
    18
    19This library considered and implemented:
    20
    21- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
    22- [OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html)
    23- [OAuth 2.0 Threat Model and Security Considerations](https://tools.ietf.org/html/rfc6819)
    24- [Proof Key for Code Exchange by OAuth Public Clients](https://tools.ietf.org/html/rfc7636)
    25- [OAuth 2.0 for Native Apps](https://tools.ietf.org/html/rfc8252)
    26- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
    27
    28OAuth2 and OpenID Connect are difficult protocols. If you want quick wins, we
    29strongly encourage you to look at [Hydra](https://github.com/ory-am/hydra).
    30Hydra is a secure, high performance, cloud native OAuth2 and OpenID Connect
    31service that integrates with every authentication method imaginable and is built
    32on top of Fosite.
    33
    34<!-- START doctoc generated TOC please keep comment here to allow auto update -->
    35<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
    36
    37**Table of Contents**
    38
    39- [Motivation](#motivation)
    40- [API Stability](#api-stability)
    41- [Example](#example)
    42- [A word on quality](#a-word-on-quality)
    43- [A word on security](#a-word-on-security)
    44- [A word on extensibility](#a-word-on-extensibility)
    45- [Installation](#installation)
    46- [Documentation](#documentation)
    47  - [Scopes](#scopes)
    48    - [`fosite.WildcardScopeStrategy`](#fositewildcardscopestrategy)
    49    - [`fosite.HierarchicScopeStrategy`](#fositehierarchicscopestrategy)
    50  - [Quickstart](#quickstart)
    51  - [Code Examples](#code-examples)
    52  - [Example Storage Implementation](#example-storage-implementation)
    53  - [Extensible handlers](#extensible-handlers)
    54  - [JWT Introspection](#jwt-introspection)
    55- [Contribute](#contribute)
    56  - [Refresh mock objects](#refresh-mock-objects)
    57- [Hall of Fame](#hall-of-fame)
    58
    59<!-- END doctoc generated TOC please keep comment here to allow auto update -->
    60
    61## Motivation
    62
    63Fosite was written because our OAuth2 and OpenID Connect service
    64[**Hydra**](https://github.com/ory-am/hydra) required a secure and extensible
    65OAuth2 library. We had to realize that nothing matching our requirements was out
    66there, so we decided to build it ourselves.
    67
    68## API Stability
    69
    70The core public API is almost stable as most changes will only touch the inner
    71workings.
    72
    73We strongly encourage vendoring fosite using
    74[dep](https://github.com/golang/dep) or comparable tools.
    75
    76## Example
    77
    78The example does not have nice visuals but it should give you an idea of what
    79you can do with Fosite and a few lines of code.
    80
    81![Authorize Code Grant](docs/authorize-code-grant.gif)
    82
    83You can run this minimalistic example by doing
    84
    85```
    86go get github.com/ory/fosite-example
    87cd $GOPATH/src/github.com/ory/fosite-example
    88dep ensure
    89go install github.com/ory/fosite-example
    90fosite-example
    91```
    92
    93There should be a server listening on [localhost:3846](https://localhost:3846/).
    94You can check out the example's source code
    95[here](https://github.com/ory/fosite-example/).
    96
    97## A word on quality
    98
    99We tried to set up as many tests as possible and test for as many cases covered
   100in the RFCs as possible. But we are only human. Please, feel free to add tests
   101for the various cases defined in the OAuth2 RFCs 6749 and 6819 or any other
   102cases that improve the tests.
   103
   104**Everyone** writing an RFC conform test that breaks with the current
   105implementation, will receive a place in the [Hall of Fame](#hall-of-fame)!
   106
   107## A word on security
   108
   109Please be aware that Fosite only secures parts of your server side security. You
   110still need to secure your apps and clients, keep your tokens safe, prevent CSRF
   111attacks, ensure database security, use valid and strong TLS certificates and
   112much more. If you need any help or advice feel free to contact our security
   113staff through [our website](https://ory.am/)!
   114
   115We have given the various specifications, especially
   116[OAuth 2.0 Threat Model and Security Considerations](https://tools.ietf.org/html/rfc6819#section-5.1.5.3),
   117a very close look and included everything we thought was in the scope of this
   118framework. Here is a complete list of things we implemented in Fosite:
   119
   120- [No Cleartext Storage of Credentials](https://tools.ietf.org/html/rfc6819#section-5.1.4.1.3)
   121- [Encryption of Credentials](https://tools.ietf.org/html/rfc6819#section-5.1.4.1.4)
   122- [Use Short Expiration Time](https://tools.ietf.org/html/rfc6819#section-5.1.5.3)
   123- [Limit Number of Usages or One-Time Usage](https://tools.ietf.org/html/rfc6819#section-5.1.5.4)
   124- [Bind Token to Client id](https://tools.ietf.org/html/rfc6819#section-5.1.5.8)
   125- [Automatic Revocation of Derived Tokens If Abuse Is Detected](https://tools.ietf.org/html/rfc6819#section-5.2.1.1)
   126- [Binding of Refresh Token to "client_id"](https://tools.ietf.org/html/rfc6819#section-5.2.2.2)
   127- [Refresh Token Rotation](https://tools.ietf.org/html/rfc6819#section-5.2.2.3)
   128- [Revocation of Refresh Tokens](https://tools.ietf.org/html/rfc6819#section-5.2.2.4)
   129- [Validate Pre-Registered "redirect_uri"](https://tools.ietf.org/html/rfc6819#section-5.2.3.5)
   130- [Binding of Authorization "code" to "client_id"](https://tools.ietf.org/html/rfc6819#section-5.2.4.4)
   131- [Binding of Authorization "code" to "redirect_uri"](https://tools.ietf.org/html/rfc6819#section-5.2.4.6)
   132- [Opaque access tokens](https://tools.ietf.org/html/rfc6749#section-1.4)
   133- [Opaque refresh tokens](https://tools.ietf.org/html/rfc6749#section-1.5)
   134- [Ensure Confidentiality of Requests](https://tools.ietf.org/html/rfc6819#section-5.1.1)
   135- [Use of Asymmetric Cryptography](https://tools.ietf.org/html/rfc6819#section-5.1.4.1.5)
   136  Fosite ensures that redirect URIs use https **except localhost** but you need
   137  to implement TLS for the token and auth endpoints yourself.
   138
   139Additionally, we added these safeguards:
   140
   141- **Enforcing random states:** Without a random-looking state or OpenID Connect
   142  nonce the request will fail.
   143- **Advanced Token Validation:** Tokens are layouted as `<key>.<signature>`
   144  where `<signature>` is created using HMAC-SHA256 using a global secret. This
   145  is what a token can look like:
   146  `/tgBeUhWlAT8tM8Bhmnx+Amf8rOYOUhrDi3pGzmjP7c=.BiV/Yhma+5moTP46anxMT6cWW8gz5R5vpC9RbpwSDdM=`
   147
   148Sections below [Section 5](https://tools.ietf.org/html/rfc6819#section-5) that
   149are not covered in the list above should be reviewed by you. If you think that a
   150specific section should be something that is covered in Fosite, feel free to
   151create an [issue](https://github.com/ory/fosite/issues). Please be aware that
   152OpenID Connect requires specific knowledge of the identity provider, which is
   153why Fosite only implements core requirements and most things must be implemented
   154by you (for example prompt, max_age, ui_locales, id_token_hint, user
   155authentication, session management, ...).
   156
   157**It is strongly encouraged to use the handlers shipped with Fosite as they
   158follow the specs and are well tested.**
   159
   160## A word on extensibility
   161
   162Fosite is extensible ... because OAuth2 is an extensible and flexible
   163**framework**. Fosite let's you register custom token and authorize endpoint
   164handlers with the security that the requests have been validated against the
   165OAuth2 specs beforehand. You can easily extend Fosite's capabilities. For
   166example, if you want to provide OpenID Connect on top of your OAuth2 stack,
   167that's no problem. Or custom assertions, what ever you like and as long as it is
   168secure. ;)
   169
   170## Installation
   171
   172[Go 1.11+](https://golang.org) must be installed on your system and it is
   173required that you have set up your GOPATH environment variable.
   174
   175```
   176go get -u github.com/ory/fosite/...
   177```
   178
   179We recommend to use [dep](https://github.com/golang/dep) to mitigate
   180compatibility breaks that come with new api versions.
   181
   182## Documentation
   183
   184There is an API documentation available at
   185[godoc.org/ory/fosite](https://godoc.org/github.com/ory/fosite).
   186
   187### Scopes
   188
   189Fosite has three strategies for matching scopes. You can replace the default
   190scope strategy if you need a custom one by implementing `fosite.ScopeStrategy`.
   191
   192Using the composer, setting a strategy is easy:
   193
   194```go
   195import "github.com/ory/fosite/compose"
   196
   197var config = &compose.Config{
   198    ScopeStrategy: fosite.HierarchicScopeStrategy,
   199}
   200```
   201
   202**Note:** To issue refresh tokens with any of the grants, you need to include
   203the `offline` scope in the OAuth2 request. This can be modified by the
   204`RefreshTokenScopes` compose configuration. When set to an empty array, _all_
   205grants will issue refresh tokens.
   206
   207#### `fosite.WildcardScopeStrategy`
   208
   209This is the default strategy, and the safest one. It is best explained by
   210looking at some examples:
   211
   212- `users.*` matches `users.read`
   213- `users.*` matches `users.read.foo`
   214- `users.read` matches `users.read`
   215- `users` does not match `users.read`
   216- `users.read.*` does not match `users.read`
   217- `users.*.*` does not match `users.read`
   218- `users.*.*` matches `users.read.own`
   219- `users.*.*` matches `users.read.own.other`
   220- `users.read.*` matches `users.read.own`
   221- `users.read.*` matches `users.read.own.other`
   222- `users.write.*` does not match `users.read.own`
   223- `users.*.bar` matches `users.baz.bar`
   224- `users.*.bar` does not `users.baz.baz.bar`
   225
   226To request `users.*`, a client must have exactly `users.*` as granted scope.
   227
   228#### `fosite.ExactScopeStrategy`
   229
   230This strategy is searching only for exact matches. It returns true iff the scope
   231is granted.
   232
   233#### `fosite.HierarchicScopeStrategy`
   234
   235This strategy is deprecated, use it with care. Again, it is best explained by
   236looking at some examples:
   237
   238- `users` matches `users`
   239- `users` matches `users.read`
   240- `users` matches `users.read.own`
   241- `users.read` matches `users.read`
   242- `users.read` matches `users.read.own`
   243- `users.read` does not match `users.write`
   244- `users.read` does not match `users.write.own`
   245
   246### Globalization
   247
   248Fosite does not natively carry translations for error messages and hints, but offers an interface that allows the consumer to define catalog bundles and an implementation to translate. This is available through the [MessageCatalog](i18n/i18n.go) interface. The functions defined are self-explanatory. The `DefaultMessageCatalog` illustrates this. Compose config has been extended to take in an instance of the `MessageCatalog`.
   249
   250#### Building translated files
   251
   252There are three possible "message key" types:
   253
   2541. Value of `RFC6749Error.ErrorField`: This is a string like `invalid_request` and correlates to most errors produced by Fosite.
   2552. Hint identifier passed into `RFC6749Error.WithHintIDOrDefaultf`: This func is not used extensively in Fosite but, in time, most `WithHint` and `WithHintf` will be replaced with this function.
   2563. Free text string format passed into `RFC6749Error.WithHint` and `RFC6749Error.WithHintf`: This function is used in Fosite and Hydra extensively and any message catalog implementation can use the format string parameter as the message key.
   257
   258An example of a message catalog can be seen in the [i18n_test.go](i18n/i18n_test.go).
   259
   260#### Generating the `en` messages file
   261
   262This is a WIP at the moment, but effectively any scripting language can be used to generate this. It would need to traverse all files in the source code and extract the possible message identifiers based on the different message key types.
   263
   264### Quickstart
   265
   266Instantiating fosite by hand can be painful. Therefore we created a few
   267convenience helpers available through the [compose package](/compose). It is
   268strongly encouraged to use these well tested composers.
   269
   270In this very basic example, we will instantiate fosite with all OpenID Connect
   271and OAuth2 handlers enabled. Please refer to the
   272[example app](https://github.com/ory/fosite-example/) for more details.
   273
   274This little code snippet sets up a full-blown OAuth2 and OpenID Connect example.
   275
   276```go
   277import "github.com/ory/fosite"
   278import "github.com/ory/fosite/compose"
   279import "github.com/ory/fosite/storage"
   280
   281// This is the example storage that contains:
   282// * an OAuth2 Client with id "my-client" and secrets "foobar" and "foobaz" capable of all oauth2 and open id connect grant and response types.
   283// * a User for the resource owner password credentials grant type with username "peter" and password "secret".
   284//
   285// You will most likely replace this with your own logic once you set up a real world application.
   286var storage = storage.NewExampleStore()
   287
   288// This secret is being used to sign access and refresh tokens as well as
   289// authorization codes. It must be exactly 32 bytes long.
   290var secret = []byte("my super secret signing password")
   291
   292privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
   293if err != nil {
   294  panic("unable to create private key")
   295}
   296
   297// check the api docs of compose.Config for further configuration options
   298var config = &compose.Config {
   299  	AccessTokenLifespan: time.Minute * 30,
   300  	// ...
   301}
   302
   303var oauth2Provider = compose.ComposeAllEnabled(config, storage, secret, privateKey)
   304
   305// The authorize endpoint is usually at "https://mydomain.com/oauth2/auth".
   306func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) {
   307	// This context will be passed to all methods. It doesn't fulfill a real purpose in the standard library but could be used
   308	// to abort database lookups or similar things.
   309	ctx := req.Context()
   310
   311	// Let's create an AuthorizeRequest object!
   312	// It will analyze the request and extract important information like scopes, response type and others.
   313	ar, err := oauth2Provider.NewAuthorizeRequest(ctx, req)
   314	if err != nil {
   315		oauth2Provider.WriteAuthorizeError(rw, ar, err)
   316		return
   317	}
   318
   319	// Normally, this would be the place where you would check if the user is logged in and gives his consent.
   320	// We're simplifying things and just checking if the request includes a valid username and password
   321	if req.Form.Get("username") != "peter" {
   322		rw.Header().Set("Content-Type", "text/html;charset=UTF-8")
   323		rw.Write([]byte(`<h1>Login page</h1>`))
   324		rw.Write([]byte(`
   325			<p>Howdy! This is the log in page. For this example, it is enough to supply the username.</p>
   326			<form method="post">
   327				<input type="text" name="username" /> <small>try peter</small><br>
   328				<input type="submit">
   329			</form>
   330		`))
   331		return
   332	}
   333
   334	// Now that the user is authorized, we set up a session. When validating / looking up tokens, we additionally get
   335	// the session. You can store anything you want in it.
   336
   337	// The session will be persisted by the store and made available when e.g. validating tokens or handling token endpoint requests.
   338	// The default OAuth2 and OpenID Connect handlers require the session to implement a few methods. Apart from that, the
   339	// session struct can be anything you want it to be.
   340	mySessionData := &fosite.DefaultSession{
   341		Username: req.Form.Get("username"),
   342	}
   343
   344	// It's also wise to check the requested scopes, e.g.:
   345	// if authorizeRequest.GetScopes().Has("admin") {
   346	//     http.Error(rw, "you're not allowed to do that", http.StatusForbidden)
   347	//     return
   348	// }
   349
   350	// Now we need to get a response. This is the place where the AuthorizeEndpointHandlers kick in and start processing the request.
   351	// NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library
   352	// to support open id connect.
   353	response, err := oauth2Provider.NewAuthorizeResponse(ctx, ar, mySessionData)
   354	if err != nil {
   355		oauth2Provider.WriteAuthorizeError(rw, ar, err)
   356		return
   357	}
   358
   359	// Awesome, now we redirect back to the client redirect uri and pass along an authorize code
   360	oauth2Provider.WriteAuthorizeResponse(rw, ar, response)
   361}
   362
   363// The token endpoint is usually at "https://mydomain.com/oauth2/token"
   364func tokenHandlerFunc(rw http.ResponseWriter, req *http.Request) {
   365	ctx := req.Context()
   366
   367	// Create an empty session object that will be passed to storage implementation to populate (unmarshal) the session into.
   368	// 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.
   369	// For an example of storage implementation that takes advantage of that, see SQL Store (fosite_store_sql.go) from ory/Hydra project.
   370	mySessionData := new(fosite.DefaultSession)
   371
   372	// This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request.
   373	accessRequest, err := oauth2Provider.NewAccessRequest(ctx, req, mySessionData)
   374	if err != nil {
   375		oauth2Provider.WriteAccessError(rw, accessRequest, err)
   376		return
   377	}
   378
   379	if mySessionData.Username == "super-admin-guy" {
   380		// do something...
   381	}
   382
   383	// Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers
   384	// and aggregate the result in response.
   385	response, err := oauth2Provider.NewAccessResponse(ctx, accessRequest)
   386	if err != nil {
   387		oauth2Provider.WriteAccessError(rw, accessRequest, err)
   388		return
   389	}
   390
   391	// All done, send the response.
   392	oauth2Provider.WriteAccessResponse(rw, accessRequest, response)
   393
   394	// The client has a valid access token now
   395}
   396
   397func someResourceProviderHandlerFunc(rw http.ResponseWriter, req *http.Request) {
   398	ctx := req.Context()
   399	requiredScope := "blogposts.create"
   400
   401	_, ar, err := oauth2Provider.IntrospectToken(ctx, fosite.AccessTokenFromRequest(req), fosite.AccessToken, new(fosite.DefaultSession), requiredScope)
   402	if err != nil {
   403		// ...
   404	}
   405
   406	// If no error occurred the token + scope is valid and you have access to:
   407	// ar.GetClient().GetID(), ar.GetGrantedScopes(), ar.GetScopes(), ar.GetSession().UserID, ar.GetRequestedAt(), ...
   408}
   409```
   410
   411### Code Examples
   412
   413Fosite provides integration tests as well as a http server example:
   414
   415- Fosite ships with an example app that runs in your browser:
   416  [Example app](https://github.com/ory/fosite-example/).
   417- If you want to check out how to enable specific handlers, check out the
   418  [integration tests](integration/).
   419
   420If you have working examples yourself, please share them with us!
   421
   422### Example Storage Implementation
   423
   424Fosite does not ship a storage implementation. This is intended, because
   425requirements vary with every environment. You can find a reference
   426implementation at [storage/memory.go](storage/memory.go). This storage fulfills
   427requirements from all OAuth2 and OpenID Connect handlers.
   428
   429### Extensible handlers
   430
   431OAuth2 is a framework. Fosite mimics this behaviour by enabling you to replace
   432existing or create new OAuth2 handlers. Of course, fosite ships handlers for all
   433OAuth2 and OpenID Connect flows.
   434
   435- **[Fosite OAuth2 Core Handlers](handler/oauth2)** implement the
   436  [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4),
   437  [Resource Owner Password Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.3),
   438  [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2),
   439  [Authorization Code Grant](https://tools.ietf.org/html/rfc6749#section-4.1),
   440  [Refresh Token Grant](https://tools.ietf.org/html/rfc6749#section-6)
   441- **[Fosite OpenID Connect Handlers](handler/openid)** implement the
   442  [Authentication using the Authorization Code Flow](http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth),
   443  [Authentication using the Implicit Flow](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth),
   444  [Authentication using the Hybrid Flow](http://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth)
   445
   446This section is missing documentation and we welcome any contributions in that
   447direction.
   448
   449### JWT Introspection
   450
   451Please note that when using the OAuth2StatelessJWTIntrospectionFactory access
   452token revocation is not possible.
   453
   454## Contribute
   455
   456You need git and golang installed on your system.
   457
   458```
   459go get -d github.com/ory/fosite
   460cd $GOPATH/src/github.com/ory/fosite
   461git status
   462git remote add myfork <url-to-your-fork>
   463go test ./...
   464```
   465
   466Simple, right? Now you are ready to go! Make sure to run `go test ./...` often,
   467detecting problems with your code rather sooner than later. Please read
   468[CONTRIBUTE.md] before creating pull requests and issues.
   469
   470### Refresh mock objects
   471
   472Run `./generate-mocks.sh` in fosite's root directory or run the contents of
   473[generate-mocks.sh] in a shell.
   474
   475## Hall of Fame
   476
   477This place is reserved for the fearless bug hunters, reviewers and contributors
   478(alphabetical order).
   479
   480- [agtorre](https://github.com/agtorre):
   481  [contributions](https://github.com/ory/fosite/issues?q=author%3Aagtorre),
   482  [participations](https://github.com/ory/fosite/issues?q=commenter%3Aagtorre).
   483- [danielchatfield](https://github.com/danielchatfield):
   484  [contributions](https://github.com/ory/fosite/issues?q=author%3Adanielchatfield),
   485  [participations](https://github.com/ory/fosite/issues?q=commenter%3Adanielchatfield).
   486- [leetal](https://github.com/leetal):
   487  [contributions](https://github.com/ory/fosite/issues?q=author%3Aleetal),
   488  [participations](https://github.com/ory/fosite/issues?q=commenter%3Aleetal).
   489- [jrossiter](https://github.com/jrossiter):
   490  [contributions](https://github.com/ory/fosite/issues?q=author%3Ajrossiter),
   491  [participations](https://github.com/ory/fosite/issues?q=commenter%3Ajrossiter).
   492- [jrossiter](https://github.com/jrossiter):
   493  [contributions](https://github.com/ory/fosite/issues?q=author%3Ajrossiter),
   494  [participations](https://github.com/ory/fosite/issues?q=commenter%3Ajrossiter).
   495- [danilobuerger](https://github.com/danilobuerger):
   496  [contributions](https://github.com/ory/fosite/issues?q=author%3Adanilobuerger),
   497  [participations](https://github.com/ory/fosite/issues?q=commenter%3Adanilobuerger).
   498
   499Find out more about the [author](https://aeneas.io/) of Fosite and Hydra, and
   500the [Ory Company](https://ory.am/).

View as plain text