1<h1 align="center"><img src="./docs/image/banner_fosite.png" alt="ORY Fosite - Security-first OAuth2 framework"></h1>
2
3[](https://travis-ci.org/ory/fosite?branch=master)
4[](https://coveralls.io/github/ory/fosite?branch=master)
5[](https://goreportcard.com/report/ory/fosite)
6
7[](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
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