github.com/wanliu/go-oauth2-server@v0.0.0-20180817021415-f928fa1580df/README.md (about)

     1  [1]: ../../../assets/blob/master/go-oauth2-server/login_screenshot.png
     2  [2]: ../../../assets/blob/master/go-oauth2-server/authorization_code_screenshot.png
     3  [3]: ../../../assets/blob/master/go-oauth2-server/implicit_screenshot.png
     4  
     5  ## Go OAuth2 Server
     6  
     7  This service implements [OAuth 2.0 specification](http://tools.ietf.org/html/rfc6749#section-4.3). Excerpts from the specification are included in this README file to describe different grant types. Please read the full spec for more detailed information.
     8  
     9  [![Travis Status for RichardKnop/go-oauth2-server](https://travis-ci.org/RichardKnop/go-oauth2-server.svg?branch=master&label=linux+build)](https://travis-ci.org/RichardKnop/go-oauth2-server)
    10  [![godoc for RichardKnop/go-oauth2-server](https://godoc.org/github.com/nathany/looper?status.svg)](http://godoc.org/github.com/wanliu/go-oauth2-server)
    11  [![goreportcard for RichardKnop/go-oauth2-server](https://goreportcard.com/badge/github.com/wanliu/go-oauth2-server)](https://goreportcard.com/report/RichardKnop/go-oauth2-server)
    12  [![codecov for RichardKnop/go-oauth2-server](https://codecov.io/gh/RichardKnop/go-oauth2-server/branch/master/graph/badge.svg)](https://codecov.io/gh/RichardKnop/go-oauth2-server)
    13  [![Codeship Status for RichardKnop/go-oauth2-server](https://codeship.com/projects/fba90e10-7020-0133-0db8-4acedf45d268/status?branch=master)](https://codeship.com/projects/116440)
    14  
    15  [![Sourcegraph for RichardKnop/go-oauth2-server](https://sourcegraph.com/github.com/wanliu/go-oauth2-server/-/badge.svg)](https://sourcegraph.com/github.com/wanliu/go-oauth2-server?badge)
    16  [![Donate Bitcoin](https://img.shields.io/badge/donate-bitcoin-orange.svg)](https://richardknop.github.io/donate/)
    17  
    18  ---
    19  
    20  * [OAuth 2.0](#oauth-20)
    21    * [Client Authentication](#client-authentication)
    22    * [Grant Types](#grant-types)
    23      * [Authorization Code](#authorization-code)
    24      * [Implicit](#implicit)
    25      * [Resource Owner Password Credentials](#resource-owner-password-credentials)
    26      * [Client Credentials](#client-credentials)
    27    * [Refreshing An Access Token](#refreshing-an-access-token)
    28    * [Token Introspection](#token-introspection)
    29  * [Plugins](#plugins)
    30  * [Session Storage](#session-storage)
    31  * [Dependencies](#dependencies)
    32  * [Setup](#setup)
    33  * [Test Data](#test-data)
    34  * [Testing](#testing)
    35  * [Docker](#docker)
    36  * [Docker Compose](#docker-compose)
    37  
    38  ## OAuth 2.0
    39  
    40  ### Client Authentication
    41  
    42  http://tools.ietf.org/html/rfc6749#section-3.2.1
    43  
    44  Clients must authenticate with client credentials (client ID and secret) when issuing requests to `/v1/oauth/tokens` endpoint. Basic HTTP authentication should be used.
    45  
    46  ### Grant Types
    47  
    48  #### Authorization Code
    49  
    50  http://tools.ietf.org/html/rfc6749#section-4.1
    51  
    52  The authorization code grant type is used to obtain both access tokens and refresh tokens and is optimized for confidential clients. Since this is a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server.
    53  
    54  ```
    55  +----------+
    56  | Resource |
    57  |   Owner  |
    58  |          |
    59  +----------+
    60       ^
    61       |
    62      (B)
    63  +----|-----+          Client Identifier      +---------------+
    64  |         -+----(A)-- & Redirection URI ---->|               |
    65  |  User-   |                                 | Authorization |
    66  |  Agent  -+----(B)-- User authenticates --->|     Server    |
    67  |          |                                 |               |
    68  |         -+----(C)-- Authorization Code ---<|               |
    69  +-|----|---+                                 +---------------+
    70    |    |                                         ^      v
    71   (A)  (C)                                        |      |
    72    |    |                                         |      |
    73    ^    v                                         |      |
    74  +---------+                                      |      |
    75  |         |>---(D)-- Authorization Code ---------'      |
    76  |  Client |          & Redirection URI                  |
    77  |         |                                             |
    78  |         |<---(E)----- Access Token -------------------'
    79  +---------+       (w/ Optional Refresh Token)
    80  ```
    81  
    82  The client initiates the flow by directing the resource owner's user-agent to the authorization endpoint. The client includes its client identifier, requested scope, local state, and a redirection URI to which the authorization server will send the user-agent back once access is granted (or denied).
    83  
    84  ```
    85  http://localhost:8080/web/authorize?client_id=test_client_1&redirect_uri=https%3A%2F%2Fwww.example.com&response_type=code&state=somestate&scope=read_write
    86  ```
    87  
    88  The authorization server authenticates the resource owner (via the user-agent).
    89  
    90  ![Log In page screenshot][1]
    91  
    92  The authorization server then establishes whether the resource owner grants or denies the client's access request.
    93  
    94  ![Authorize page screenshot][2]
    95  
    96  If the request fails due to a missing, invalid, or mismatching redirection URI, or if the client identifier is missing or invalid, the authorization server SHOULD inform the resource owner of the error and MUST NOT automatically redirect the user-agent to the invalid redirection URI.
    97  
    98  If the resource owner denies the access request or if the request fails for reasons other than a missing or invalid redirection URI, the authorization server informs the client by adding the error parameter to the query component of the redirection URI.
    99  
   100  ```
   101  https://www.example.com/?error=access_denied&state=somestate
   102  ```
   103  
   104  Assuming the resource owner grants access, the authorization server redirects the user-agent back to the client using the redirection URI provided earlier (in the request or during client registration). The redirection URI includes an authorization code and any local state provided by the client earlier.
   105  
   106  ```
   107  https://www.example.com/?code=7afb1c55-76e4-4c76-adb7-9d657cb47a27&state=somestate
   108  ```
   109  
   110  The client requests an access token from the authorization server's token endpoint by including the authorization code received in the previous step. When making the request, the client authenticates with the authorization server. The client includes the redirection URI used to obtain the authorization code for verification.
   111  
   112  ```sh
   113  curl --compressed -v localhost:8080/v1/oauth/tokens \
   114  	-u test_client_1:test_secret \
   115  	-d "grant_type=authorization_code" \
   116  	-d "code=7afb1c55-76e4-4c76-adb7-9d657cb47a27" \
   117  	-d "redirect_uri=https://www.example.com"
   118  ```
   119  The authorization server authenticates the client, validates the authorization code, and ensures that the redirection URI received matches the URI used to redirect the client before. If valid, the authorization server responds back with an access token and, optionally, a refresh token.
   120  
   121  ```json
   122  {
   123    "user_id": 1,
   124    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   125    "expires_in": 3600,
   126    "token_type": "Bearer",
   127    "scope": "read_write",
   128    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   129  }
   130  ```
   131  
   132  #### Implicit
   133  
   134  http://tools.ietf.org/html/rfc6749#section-4.2
   135  
   136  The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript.
   137  
   138  Since this is a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server.
   139  
   140  Unlike the authorization code grant type, in which the client makes separate requests for authorization and for an access token, the client receives the access token as the result of the authorization request.
   141  
   142  The implicit grant type does not include client authentication, and relies on the presence of the resource owner and the registration of the redirection URI.  Because the access token is encoded into the redirection URI, it may be exposed to the resource owner and other applications residing on the same device.
   143  
   144  ```
   145  +----------+
   146  | Resource |
   147  |  Owner   |
   148  |          |
   149  +----------+
   150       ^
   151       |
   152      (B)
   153  +----|-----+          Client Identifier     +---------------+
   154  |         -+----(A)-- & Redirection URI --->|               |
   155  |  User-   |                                | Authorization |
   156  |  Agent  -|----(B)-- User authenticates -->|     Server    |
   157  |          |                                |               |
   158  |          |<---(C)--- Redirection URI ----<|               |
   159  |          |          with Access Token     +---------------+
   160  |          |            in Fragment
   161  |          |                                +---------------+
   162  |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
   163  |          |          without Fragment      |     Client    |
   164  |          |                                |    Resource   |
   165  |     (F)  |<---(E)------- Script ---------<|               |
   166  |          |                                +---------------+
   167  +-|--------+
   168    |    |
   169   (A)  (G) Access Token
   170    |    |
   171    ^    v
   172  +---------+
   173  |         |
   174  |  Client |
   175  |         |
   176  +---------+
   177  ```
   178  
   179  The client initiates the flow by directing the resource owner's user-agent to the authorization endpoint. The client includes its client identifier, requested scope, local state, and a redirection URI to which the authorization server will send the user-agent back once access is granted (or denied).
   180  
   181  ```
   182  http://localhost:8080/web/authorize?client_id=test_client_1&redirect_uri=https%3A%2F%2Fwww.example.com&response_type=token&state=somestate&scope=read_write
   183  ```
   184  
   185  The authorization server authenticates the resource owner (via the user-agent).
   186  
   187  ![Log In page screenshot][1]
   188  
   189  The authorization server then establishes whether the resource owner grants or denies the client's access request.
   190  
   191  ![Authorize page screenshot][3]
   192  
   193  If the request fails due to a missing, invalid, or mismatching redirection URI, or if the client identifier is missing or invalid, the authorization server SHOULD inform the resource owner of the error and MUST NOT automatically redirect the user-agent to the invalid redirection URI.
   194  
   195  If the resource owner denies the access request or if the request fails for reasons other than a missing or invalid redirection URI, the authorization server informs the client by adding the following parameters to the fragment component of the redirection URI.
   196  
   197  ```
   198  https://www.example.com/#error=access_denied&state=somestate
   199  ```
   200  
   201  Assuming the resource owner grants access, the authorization server redirects the user-agent back to the client using the redirection URI provided earlier.  The redirection URI includes he access token in the URI fragment.
   202  
   203  ```
   204  https://www.example.com/#access_token=087902d5-29e7-417b-a339-b57a60d6742a&expires_in=3600&scope=read_write&state=somestate&token_type=Bearer
   205  ```
   206  
   207  The user-agent follows the redirection instructions by making a request to the web-hosted client resource (which does not include the fragment per [RFC2616]).  The user-agent retains the fragment information locally.
   208  
   209  The web-hosted client resource returns a web page (typically an HTML document with an embedded script) capable of accessing the full redirection URI including the fragment retained by the user-agent, and extracting the access token (and other parameters) contained in the fragment.
   210  
   211  The user-agent executes the script provided by the web-hosted client resource locally, which extracts the access token.
   212  
   213  The user-agent passes the access token to the client.
   214  
   215  #### Resource Owner Password Credentials
   216  
   217  http://tools.ietf.org/html/rfc6749#section-4.3
   218  
   219  The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application. The authorization server should take special care when enabling this grant type and only allow it when other flows are not viable.
   220  
   221  This grant type is suitable for clients capable of obtaining the resource owner's credentials (username and password, typically using an interactive form). It is also used to migrate existing clients using direct authentication schemes such as HTTP Basic or Digest authentication to OAuth by converting the stored credentials to an access token.
   222  
   223  ```
   224  +----------+
   225  | Resource |
   226  |  Owner   |
   227  |          |
   228  +----------+
   229       v
   230       |    Resource Owner
   231       (A) Password Credentials
   232       |
   233       v
   234  +---------+                                  +---------------+
   235  |         |>--(B)---- Resource Owner ------->|               |
   236  |         |         Password Credentials     | Authorization |
   237  | Client  |                                  |     Server    |
   238  |         |<--(C)---- Access Token ---------<|               |
   239  |         |    (w/ Optional Refresh Token)   |               |
   240  +---------+                                  +---------------+
   241  
   242  ```
   243  
   244  The resource owner provides the client with its username and password.
   245  
   246  The client requests an access token from the authorization server's token endpoint by including the credentials received from the resource owner. When making the request, the client authenticates with the authorization server.
   247  
   248  ```sh
   249  curl --compressed -v localhost:8080/v1/oauth/tokens \
   250  	-u test_client_1:test_secret \
   251  	-d "grant_type=password" \
   252  	-d "username=test@username" \
   253  	-d "password=test_password" \
   254  	-d "scope=read_write"
   255  ```
   256  
   257  The authorization server authenticates the client and validates the resource owner credentials, and if valid, issues an access token.
   258  
   259  ```json
   260  {
   261    "user_id": 1,
   262    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   263    "expires_in": 3600,
   264    "token_type": "Bearer",
   265    "scope": "read_write",
   266    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   267  }
   268  ```
   269  
   270  #### Client Credentials
   271  
   272  http://tools.ietf.org/html/rfc6749#section-4.4
   273  
   274  The client can request an access token using only its client credentials (or other supported means of authentication) when the client is requesting access to the protected resources under its control, or those of another resource owner that have been previously arranged with the authorization server (the method of which is beyond the scope of this specification).
   275  
   276  The client credentials grant type MUST only be used by confidential clients.
   277  
   278  ```
   279  +---------+                                  +---------------+
   280  |         |                                  |               |
   281  |         |>--(A)- Client Authentication --->| Authorization |
   282  | Client  |                                  |     Server    |
   283  |         |<--(B)---- Access Token ---------<|               |
   284  |         |                                  |               |
   285  +---------+                                  +---------------+
   286  ```
   287  
   288  The client authenticates with the authorization server and requests an access token from the token endpoint.
   289  
   290  ```sh
   291  curl --compressed -v localhost:8080/v1/oauth/tokens \
   292  	-u test_client_1:test_secret \
   293  	-d "grant_type=client_credentials" \
   294  	-d "scope=read_write"
   295  ```
   296  
   297  The authorization server authenticates the client, and if valid, issues an access token.
   298  
   299  ```json
   300  {
   301    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   302    "expires_in": 3600,
   303    "token_type": "Bearer",
   304    "scope": "read_write",
   305    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   306  }
   307  ```
   308  
   309  ### Refreshing An Access Token
   310  
   311  http://tools.ietf.org/html/rfc6749#section-6
   312  
   313  If the authorization server issued a refresh token to the client, the client can make a refresh request to the token endpoint in order to refresh the access token.
   314  
   315  ```sh
   316  curl --compressed -v localhost:8080/v1/oauth/tokens \
   317  	-u test_client_1:test_secret \
   318  	-d "grant_type=refresh_token" \
   319  	-d "refresh_token=6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   320  ```
   321  
   322  The authorization server MUST:
   323  
   324  * require client authentication for confidential clients or for any client that was issued client credentials (or with other authentication requirements),
   325  
   326  * authenticate the client if client authentication is included and ensure that the refresh token was issued to the authenticated client, and
   327  
   328  * validate the refresh token.
   329  
   330  If valid and authorized, the authorization server issues an access token.
   331  
   332  ```json
   333  {
   334    "user_id": 1,
   335    "access_token": "1f962bd5-7890-435d-b619-584b6aa32e6c",
   336    "expires_in": 3600,
   337    "token_type": "Bearer",
   338    "scope": "read_write",
   339    "refresh_token": "3a6b45b8-9d29-4cba-8a1b-0093e8a2b933"
   340  }
   341  ```
   342  
   343  The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token.  The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client.  If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.
   344  
   345  ### Token Introspection
   346  
   347  https://tools.ietf.org/html/rfc7662
   348  
   349  If the authorization server issued a access token or refresh token to the client, the client can make a request to the introspect endpoint in order to learn meta-information about a token.
   350  
   351  ```sh
   352  curl --compressed -v localhost:8080/v1/oauth/introspect \
   353  	-u test_client_1:test_secret \
   354  	-d "token=00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c" \
   355  	-d "token_type_hint=access_token"
   356  ```
   357  
   358  The authorization server responds meta-information about a token.
   359  
   360  ```json
   361  {
   362    "active": true,
   363    "scope": "read_write",
   364    "client_id": "test_client_1",
   365    "username": "test@username",
   366    "token_type": "Bearer",
   367    "exp": 1454868090
   368  }
   369  ```
   370  
   371  ## Plugins
   372  
   373  This server is easily extended or modified through the use of plugins. Four services, [health](https://github.com/wanliu/go-oauth2-server/tree/master/health), [oauth](https://github.com/wanliu/go-oauth2-server/tree/master/oauth), [session](https://github.com/wanliu/go-oauth2-server/tree/master/session) and [web](https://github.com/wanliu/go-oauth2-server/tree/master/web) are available for modification.
   374  
   375  In order to implement a plugin:
   376  1. Create your own interface that implements all of methods of the service you are replacing.
   377  2. Modify `cmd/run_server.go` to use your service by calling the `session.Use[service-you-are-replaceing]Service(yourCustomService.NewService())` before the services are initialized via `services.Init(cnf, db)`.
   378  
   379  For example, to implement an available [redis session storage plugin](https://github.com/adam-hanna/redis-sessions):
   380  
   381  ~~~go
   382  // $ go get https://github.com/adam-hanna/redis-sessions
   383  //
   384  // cmd/run_server.go
   385  import (
   386      ...
   387      "github.com/adam-hanna/redis-sessions/redis"
   388      ...
   389  )
   390  
   391  // RunServer runs the app
   392  func RunServer(configBackend string) error {
   393      ...
   394  
   395      // configure redis for session store
   396      sessionSecrets := make([][]byte, 1)
   397      sessionSecrets[0] = []byte(cnf.Session.Secret)
   398      redisConfig := redis.ConfigType{
   399          Size:           10,
   400          Network:        "tcp",
   401          Address:        ":6379",
   402          Password:       "",
   403          SessionSecrets: sessionSecrets,
   404      }
   405  
   406      // start the services
   407      services.UseSessionService(redis.NewService(cnf, redisConfig))
   408      if err := services.InitServices(cnf, db); err != nil {
   409          return err
   410      }
   411      defer services.CloseServices()
   412  
   413      ...
   414  }
   415  ~~~
   416  
   417  ## Session Storage
   418  
   419  By default, this server implements in-memory, cookie sessions via [gorilla sessions](https://github.com/gorilla/sessions).
   420  
   421  However, because the session service can be replaced via a plugin, any of the available [gorilla sessions store implementations](https://github.com/gorilla/sessions#store-implementations) can be wrapped by `session.ServiceInterface`.
   422  
   423  ## Dependencies
   424  
   425  According to [Go 1.5 Vendor experiment](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo), all dependencies are stored in the vendor directory. This approach is called `vendoring` and is the best practice for Go projects to lock versions of dependencies in order to achieve reproducible builds.
   426  
   427  To update dependencies during development:
   428  
   429  ```sh
   430  make update-deps
   431  ```
   432  
   433  To install dependencies:
   434  
   435  ```sh
   436  make install-deps
   437  ```
   438  
   439  ## Setup
   440  
   441  For distributed config storage you can use either etcd or consul (etcd being the default)
   442  
   443  If you are developing on OSX, install `etcd` or `consul`, `Postgres` and `nats-streaming-server`:
   444  
   445  ### etcd
   446  
   447  ```sh
   448  brew install etcd
   449  ```
   450  
   451  Load a development configuration into `etcd`:
   452  
   453  ```sh
   454  ETCDCTL_API=3 etcdctl put /config/go_oauth2_server.json '{
   455    "Database": {
   456      "Type": "postgres",
   457      "Host": "localhost",
   458      "Port": 5432,
   459      "User": "go_oauth2_server",
   460      "Password": "",
   461      "DatabaseName": "go_oauth2_server",
   462      "MaxIdleConns": 5,
   463      "MaxOpenConns": 5
   464    },
   465    "Oauth": {
   466      "AccessTokenLifetime": 3600,
   467      "RefreshTokenLifetime": 1209600,
   468      "AuthCodeLifetime": 3600
   469    },
   470    "Session": {
   471      "Secret": "test_secret",
   472      "Path": "/",
   473      "MaxAge": 604800,
   474      "HTTPOnly": true
   475    },
   476    "IsDevelopment": true
   477  }'
   478  ```
   479  
   480  If you are using etcd API version 3, use `etcdctl put` instead of `etcdctl set`.
   481  
   482  Check the config was loaded properly:
   483  
   484  ```sh
   485  etcdctl get /config/go_oauth2_server.json
   486  ```
   487  
   488  ### consul
   489  
   490  ```sh
   491  brew install consul
   492  ```
   493  
   494  Load a development configuration into `consul`:
   495  
   496  ```sh
   497  consul kv put /config/go_oauth2_server.json '{
   498    "Database": {
   499      "Type": "postgres",
   500      "Host": "localhost",
   501      "Port": 5432,
   502      "User": "go_oauth2_server",
   503      "Password": "",
   504      "DatabaseName": "go_oauth2_server",
   505      "MaxIdleConns": 5,
   506      "MaxOpenConns": 5
   507    },
   508    "Oauth": {
   509      "AccessTokenLifetime": 3600,
   510      "RefreshTokenLifetime": 1209600,
   511      "AuthCodeLifetime": 3600
   512    },
   513    "Session": {
   514      "Secret": "test_secret",
   515      "Path": "/",
   516      "MaxAge": 604800,
   517      "HTTPOnly": true
   518    },
   519    "IsDevelopment": true
   520  }'
   521  ```
   522  
   523  Check the config was loaded properly:
   524  
   525  ```sh
   526  consul kv get /config/go_oauth2_server.json
   527  ```
   528  
   529  ### Postgres
   530  
   531  ```sh
   532  brew install postgres
   533  ```
   534  
   535  You might want to create a `Postgres` database:
   536  
   537  ```sh
   538  createuser --createdb go_oauth2_server
   539  createdb -U go_oauth2_server go_oauth2_server
   540  ```
   541  
   542  ## Compile & Run
   543  
   544  Compile the app:
   545  
   546  ```sh
   547  go install .
   548  ```
   549  
   550  The binary accepts an optional flag of `--configBackend` which can be set to `etcd | consul`, defaults to `etcd`
   551  
   552  Run migrations:
   553  
   554  ```sh
   555  go-oauth2-server migrate
   556  ```
   557  
   558  And finally, run the app:
   559  
   560  ```sh
   561  go-oauth2-server runserver
   562  ```
   563  
   564  When deploying, you can set etcd related environment variables:
   565  
   566  * `ETCD_ENDPOINTS`
   567  * `ETCD_CERT_FILE`
   568  * `ETCD_KEY_FILE`
   569  * `ETCD_CA_FILE`
   570  * `ETCD_CONFIG_PATH`
   571  
   572  You can also set consul related variables
   573  
   574  * `CONSUL_ENDPOINT`
   575  * `CONSUL_CERT_FILE`
   576  * `CONSUL_KEY_FILE`
   577  * `CONSUL_CA_FILE`
   578  * `CONSUL_CONFIG_PATH`
   579  
   580  and the equivalent above commands would be
   581  
   582  ```sh
   583  go-oauth2-server --configBackend consul migrate
   584  ```
   585  ```sh
   586  go-oauth2-server --configBackend consul runserver
   587  ```
   588  
   589  ## Testing
   590  
   591  I have used a mix of unit and functional tests so you need to have `sqlite` installed in order for the tests to run successfully as the suite creates an in-memory database.
   592  
   593  To run tests:
   594  
   595  ```sh
   596  make test
   597  ```
   598  
   599  ## Docker
   600  
   601  Build a Docker image and run the app in a container:
   602  
   603  ```sh
   604  docker build -t go-oauth2-server:latest .
   605  docker run -e ETCD_ENDPOINTs=localhost:2379 -p 8080:8080 --name go-oauth2-server go-oauth2-server:latest
   606  ```
   607  
   608  You can load fixtures with `docker exec` command:
   609  
   610  ```sh
   611  docker exec <container_id> /go/bin/go-oauth2-server loaddata \
   612    oauth/fixtures/scopes.yml \
   613    oauth/fixtures/roles.yml \
   614    oauth/fixtures/test_clients.yml
   615  ```
   616  
   617  ## Docker Compose
   618  
   619  You can use [docker-compose](https://docs.docker.com/compose/) to start the app, postgres, etcd in separate linked containers:
   620  
   621  ```sh
   622  docker-compose up
   623  ```
   624  
   625  During `docker-compose up` process all configuration and fixtures will be loaded. After successful up you can check, that app is running using for example the health check request:
   626  
   627  ```sh
   628  curl --compressed -v localhost:8080/v1/health
   629  ```