github.com/RichardKnop/go-oauth2-server@v1.0.5-0.20201019163316-d02a401490d0/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  [4]: http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png
     5  [5]: http://richardknop.com/images/btcaddress.png
     6  
     7  ## Go OAuth2 Server
     8  
     9  This service implements [OAuth 2.0 specification](https://tools.ietf.org/html/rfc6749). Excerpts from the specification are included in this README file to describe different grant types. Please read the full spec for more detailed information.
    10  
    11  [![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)
    12  [![godoc for RichardKnop/go-oauth2-server](https://godoc.org/github.com/nathany/looper?status.svg)](http://godoc.org/github.com/RichardKnop/go-oauth2-server)
    13  [![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)
    14  
    15  [![Sourcegraph for RichardKnop/go-oauth2-server](https://sourcegraph.com/github.com/RichardKnop/go-oauth2-server/-/badge.svg)](https://sourcegraph.com/github.com/RichardKnop/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    * [etcd](#etcd)
    34    * [consul](#consul)
    35    * [postgres](#postgres)
    36  * [Compile & Run Data](#compile--run)
    37  * [Testing](#testing)
    38  * [Docker](#docker)
    39  * [Docker Compose](#docker-compose)
    40  * [Supporting the project](#supporting-the-project)
    41  
    42  ## OAuth 2.0
    43  
    44  ### Client Authentication
    45  
    46  http://tools.ietf.org/html/rfc6749#section-3.2.1
    47  
    48  Clients must authenticate with client credentials (client ID and secret) when issuing requests to `/v1/oauth/tokens` endpoint. Basic HTTP authentication should be used.
    49  
    50  ### Grant Types
    51  
    52  #### Authorization Code
    53  
    54  http://tools.ietf.org/html/rfc6749#section-4.1
    55  
    56  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.
    57  
    58  ```
    59  +----------+
    60  | Resource |
    61  |   Owner  |
    62  |          |
    63  +----------+
    64       ^
    65       |
    66      (B)
    67  +----|-----+          Client Identifier      +---------------+
    68  |         -+----(A)-- & Redirection URI ---->|               |
    69  |  User-   |                                 | Authorization |
    70  |  Agent  -+----(B)-- User authenticates --->|     Server    |
    71  |          |                                 |               |
    72  |         -+----(C)-- Authorization Code ---<|               |
    73  +-|----|---+                                 +---------------+
    74    |    |                                         ^      v
    75   (A)  (C)                                        |      |
    76    |    |                                         |      |
    77    ^    v                                         |      |
    78  +---------+                                      |      |
    79  |         |>---(D)-- Authorization Code ---------'      |
    80  |  Client |          & Redirection URI                  |
    81  |         |                                             |
    82  |         |<---(E)----- Access Token -------------------'
    83  +---------+       (w/ Optional Refresh Token)
    84  ```
    85  
    86  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).
    87  
    88  ```
    89  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
    90  ```
    91  
    92  The authorization server authenticates the resource owner (via the user-agent).
    93  
    94  ![Log In page screenshot][1]
    95  
    96  The authorization server then establishes whether the resource owner grants or denies the client's access request.
    97  
    98  ![Authorize page screenshot][2]
    99  
   100  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.
   101  
   102  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.
   103  
   104  ```
   105  https://www.example.com/?error=access_denied&state=somestate
   106  ```
   107  
   108  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.
   109  
   110  ```
   111  https://www.example.com/?code=7afb1c55-76e4-4c76-adb7-9d657cb47a27&state=somestate
   112  ```
   113  
   114  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.
   115  
   116  ```sh
   117  curl --compressed -v localhost:8080/v1/oauth/tokens \
   118  	-u test_client_1:test_secret \
   119  	-d "grant_type=authorization_code" \
   120  	-d "code=7afb1c55-76e4-4c76-adb7-9d657cb47a27" \
   121  	-d "redirect_uri=https://www.example.com"
   122  ```
   123  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.
   124  
   125  ```json
   126  {
   127    "user_id": "1",
   128    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   129    "expires_in": 3600,
   130    "token_type": "Bearer",
   131    "scope": "read_write",
   132    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   133  }
   134  ```
   135  
   136  #### Implicit
   137  
   138  http://tools.ietf.org/html/rfc6749#section-4.2
   139  
   140  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.
   141  
   142  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.
   143  
   144  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.
   145  
   146  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.
   147  
   148  ```
   149  +----------+
   150  | Resource |
   151  |  Owner   |
   152  |          |
   153  +----------+
   154       ^
   155       |
   156      (B)
   157  +----|-----+          Client Identifier     +---------------+
   158  |         -+----(A)-- & Redirection URI --->|               |
   159  |  User-   |                                | Authorization |
   160  |  Agent  -|----(B)-- User authenticates -->|     Server    |
   161  |          |                                |               |
   162  |          |<---(C)--- Redirection URI ----<|               |
   163  |          |          with Access Token     +---------------+
   164  |          |            in Fragment
   165  |          |                                +---------------+
   166  |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
   167  |          |          without Fragment      |     Client    |
   168  |          |                                |    Resource   |
   169  |     (F)  |<---(E)------- Script ---------<|               |
   170  |          |                                +---------------+
   171  +-|--------+
   172    |    |
   173   (A)  (G) Access Token
   174    |    |
   175    ^    v
   176  +---------+
   177  |         |
   178  |  Client |
   179  |         |
   180  +---------+
   181  ```
   182  
   183  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).
   184  
   185  ```
   186  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
   187  ```
   188  
   189  The authorization server authenticates the resource owner (via the user-agent).
   190  
   191  ![Log In page screenshot][1]
   192  
   193  The authorization server then establishes whether the resource owner grants or denies the client's access request.
   194  
   195  ![Authorize page screenshot][3]
   196  
   197  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.
   198  
   199  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.
   200  
   201  ```
   202  https://www.example.com/#error=access_denied&state=somestate
   203  ```
   204  
   205  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.
   206  
   207  ```
   208  https://www.example.com/#access_token=087902d5-29e7-417b-a339-b57a60d6742a&expires_in=3600&scope=read_write&state=somestate&token_type=Bearer
   209  ```
   210  
   211  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.
   212  
   213  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.
   214  
   215  The user-agent executes the script provided by the web-hosted client resource locally, which extracts the access token.
   216  
   217  The user-agent passes the access token to the client.
   218  
   219  #### Resource Owner Password Credentials
   220  
   221  http://tools.ietf.org/html/rfc6749#section-4.3
   222  
   223  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.
   224  
   225  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.
   226  
   227  ```
   228  +----------+
   229  | Resource |
   230  |  Owner   |
   231  |          |
   232  +----------+
   233       v
   234       |    Resource Owner
   235       (A) Password Credentials
   236       |
   237       v
   238  +---------+                                  +---------------+
   239  |         |>--(B)---- Resource Owner ------->|               |
   240  |         |         Password Credentials     | Authorization |
   241  | Client  |                                  |     Server    |
   242  |         |<--(C)---- Access Token ---------<|               |
   243  |         |    (w/ Optional Refresh Token)   |               |
   244  +---------+                                  +---------------+
   245  
   246  ```
   247  
   248  The resource owner provides the client with its username and password.
   249  
   250  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.
   251  
   252  ```sh
   253  curl --compressed -v localhost:8080/v1/oauth/tokens \
   254  	-u test_client_1:test_secret \
   255  	-d "grant_type=password" \
   256  	-d "username=test@user" \
   257  	-d "password=test_password" \
   258  	-d "scope=read_write"
   259  ```
   260  
   261  The authorization server authenticates the client and validates the resource owner credentials, and if valid, issues an access token.
   262  
   263  ```json
   264  {
   265    "user_id": "1",
   266    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   267    "expires_in": 3600,
   268    "token_type": "Bearer",
   269    "scope": "read_write",
   270    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   271  }
   272  ```
   273  
   274  #### Client Credentials
   275  
   276  http://tools.ietf.org/html/rfc6749#section-4.4
   277  
   278  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).
   279  
   280  The client credentials grant type MUST only be used by confidential clients.
   281  
   282  ```
   283  +---------+                                  +---------------+
   284  |         |                                  |               |
   285  |         |>--(A)- Client Authentication --->| Authorization |
   286  | Client  |                                  |     Server    |
   287  |         |<--(B)---- Access Token ---------<|               |
   288  |         |                                  |               |
   289  +---------+                                  +---------------+
   290  ```
   291  
   292  The client authenticates with the authorization server and requests an access token from the token endpoint.
   293  
   294  ```sh
   295  curl --compressed -v localhost:8080/v1/oauth/tokens \
   296  	-u test_client_1:test_secret \
   297  	-d "grant_type=client_credentials" \
   298  	-d "scope=read_write"
   299  ```
   300  
   301  The authorization server authenticates the client, and if valid, issues an access token.
   302  
   303  ```json
   304  {
   305    "access_token": "00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c",
   306    "expires_in": 3600,
   307    "token_type": "Bearer",
   308    "scope": "read_write",
   309    "refresh_token": "6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   310  }
   311  ```
   312  
   313  ### Refreshing An Access Token
   314  
   315  http://tools.ietf.org/html/rfc6749#section-6
   316  
   317  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.
   318  
   319  ```sh
   320  curl --compressed -v localhost:8080/v1/oauth/tokens \
   321  	-u test_client_1:test_secret \
   322  	-d "grant_type=refresh_token" \
   323  	-d "refresh_token=6fd8d272-375a-4d8a-8d0f-43367dc8b791"
   324  ```
   325  
   326  The authorization server MUST:
   327  
   328  * require client authentication for confidential clients or for any client that was issued client credentials (or with other authentication requirements),
   329  
   330  * authenticate the client if client authentication is included and ensure that the refresh token was issued to the authenticated client, and
   331  
   332  * validate the refresh token.
   333  
   334  If valid and authorized, the authorization server issues an access token.
   335  
   336  ```json
   337  {
   338    "user_id": "1",
   339    "access_token": "1f962bd5-7890-435d-b619-584b6aa32e6c",
   340    "expires_in": 3600,
   341    "token_type": "Bearer",
   342    "scope": "read_write",
   343    "refresh_token": "3a6b45b8-9d29-4cba-8a1b-0093e8a2b933"
   344  }
   345  ```
   346  
   347  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.
   348  
   349  ### Token Introspection
   350  
   351  https://tools.ietf.org/html/rfc7662
   352  
   353  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.
   354  
   355  ```sh
   356  curl --compressed -v localhost:8080/v1/oauth/introspect \
   357  	-u test_client_1:test_secret \
   358  	-d "token=00ccd40e-72ca-4e79-a4b6-67c95e2e3f1c" \
   359  	-d "token_type_hint=access_token"
   360  ```
   361  
   362  The authorization server responds meta-information about a token.
   363  
   364  ```json
   365  {
   366    "active": true,
   367    "scope": "read_write",
   368    "client_id": "test_client_1",
   369    "username": "test@username",
   370    "token_type": "Bearer",
   371    "exp": 1454868090
   372  }
   373  ```
   374  
   375  ## Plugins
   376  
   377  This server is easily extended or modified through the use of plugins. Four services, [health](https://github.com/RichardKnop/go-oauth2-server/tree/master/health), [oauth](https://github.com/RichardKnop/go-oauth2-server/tree/master/oauth), [session](https://github.com/RichardKnop/go-oauth2-server/tree/master/session) and [web](https://github.com/RichardKnop/go-oauth2-server/tree/master/web) are available for modification.
   378  
   379  In order to implement a plugin:
   380  1. Create your own interface that implements all of methods of the service you are replacing.
   381  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)`.
   382  
   383  For example, to implement an available [redis session storage plugin](https://github.com/adam-hanna/redis-sessions):
   384  
   385  ~~~go
   386  // $ go get https://github.com/adam-hanna/redis-sessions
   387  //
   388  // cmd/run_server.go
   389  import (
   390      ...
   391      "github.com/adam-hanna/redis-sessions/redis"
   392      ...
   393  )
   394  
   395  // RunServer runs the app
   396  func RunServer(configBackend string) error {
   397      ...
   398  
   399      // configure redis for session store
   400      sessionSecrets := make([][]byte, 1)
   401      sessionSecrets[0] = []byte(cnf.Session.Secret)
   402      redisConfig := redis.ConfigType{
   403          Size:           10,
   404          Network:        "tcp",
   405          Address:        ":6379",
   406          Password:       "",
   407          SessionSecrets: sessionSecrets,
   408      }
   409  
   410      // start the services
   411      services.UseSessionService(redis.NewService(cnf, redisConfig))
   412      if err := services.InitServices(cnf, db); err != nil {
   413          return err
   414      }
   415      defer services.CloseServices()
   416  
   417      ...
   418  }
   419  ~~~
   420  
   421  ## Session Storage
   422  
   423  By default, this server implements in-memory, cookie sessions via [gorilla sessions](https://github.com/gorilla/sessions).
   424  
   425  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`.
   426  
   427  ## Dependencies
   428  
   429  Since Go 1.11, a new recommended dependency management system is via [modules](https://github.com/golang/go/wiki/Modules).
   430  
   431  This is one of slight weaknesses of Go as dependency management is not a solved problem. Previously Go was officially recommending to use the [dep tool](https://github.com/golang/dep) but that has been abandoned now in favor of modules.
   432  
   433  ## Setup
   434  
   435  For distributed config storage you can use either etcd or consul (etcd being the default)
   436  
   437  If you are developing on OSX, install `etcd` or `consul`, `Postgres` and `nats-streaming-server`:
   438  
   439  ### etcd
   440  
   441  ```sh
   442  brew install etcd
   443  ```
   444  
   445  Load a development configuration into `etcd`:
   446  
   447  ```sh
   448  ETCDCTL_API=3 etcdctl put /config/go_oauth2_server.json '{
   449    "Database": {
   450      "Type": "postgres",
   451      "Host": "localhost",
   452      "Port": 5432,
   453      "User": "go_oauth2_server",
   454      "Password": "",
   455      "DatabaseName": "go_oauth2_server",
   456      "MaxIdleConns": 5,
   457      "MaxOpenConns": 5
   458    },
   459    "Oauth": {
   460      "AccessTokenLifetime": 3600,
   461      "RefreshTokenLifetime": 1209600,
   462      "AuthCodeLifetime": 3600
   463    },
   464    "Session": {
   465      "Secret": "test_secret",
   466      "Path": "/",
   467      "MaxAge": 604800,
   468      "HTTPOnly": true
   469    },
   470    "IsDevelopment": true
   471  }'
   472  ```
   473  
   474  If you are using etcd API version 3, use `etcdctl put` instead of `etcdctl set`.
   475  
   476  Check the config was loaded properly:
   477  
   478  ```sh
   479  ETCDCTL_API=3 etcdctl get /config/go_oauth2_server.json
   480  ```
   481  
   482  ### consul
   483  
   484  ```sh
   485  brew install consul
   486  ```
   487  
   488  Load a development configuration into `consul`:
   489  
   490  ```sh
   491  consul kv put /config/go_oauth2_server.json '{
   492    "Database": {
   493      "Type": "postgres",
   494      "Host": "localhost",
   495      "Port": 5432,
   496      "User": "go_oauth2_server",
   497      "Password": "",
   498      "DatabaseName": "go_oauth2_server",
   499      "MaxIdleConns": 5,
   500      "MaxOpenConns": 5
   501    },
   502    "Oauth": {
   503      "AccessTokenLifetime": 3600,
   504      "RefreshTokenLifetime": 1209600,
   505      "AuthCodeLifetime": 3600
   506    },
   507    "Session": {
   508      "Secret": "test_secret",
   509      "Path": "/",
   510      "MaxAge": 604800,
   511      "HTTPOnly": true
   512    },
   513    "IsDevelopment": true
   514  }'
   515  ```
   516  
   517  Check the config was loaded properly:
   518  
   519  ```sh
   520  consul kv get /config/go_oauth2_server.json
   521  ```
   522  
   523  ### Postgres
   524  
   525  ```sh
   526  brew install postgres
   527  ```
   528  
   529  You might want to create a `Postgres` database:
   530  
   531  ```sh
   532  createuser --createdb go_oauth2_server
   533  createdb -U go_oauth2_server go_oauth2_server
   534  ```
   535  
   536  ## Compile & Run
   537  
   538  Compile the app:
   539  
   540  ```sh
   541  go install .
   542  ```
   543  
   544  The binary accepts an optional flag of `--configBackend` which can be set to `etcd | consul`, defaults to `etcd`
   545  
   546  Run migrations:
   547  
   548  ```sh
   549  go-oauth2-server migrate
   550  ```
   551  
   552  And finally, run the app:
   553  
   554  ```sh
   555  go-oauth2-server runserver
   556  ```
   557  
   558  When deploying, you can set etcd related environment variables:
   559  
   560  * `ETCD_ENDPOINTS`
   561  * `ETCD_CERT_FILE`
   562  * `ETCD_KEY_FILE`
   563  * `ETCD_CA_FILE`
   564  * `ETCD_CONFIG_PATH`
   565  
   566  You can also set consul related variables
   567  
   568  * `CONSUL_ENDPOINT`
   569  * `CONSUL_CERT_FILE`
   570  * `CONSUL_KEY_FILE`
   571  * `CONSUL_CA_FILE`
   572  * `CONSUL_CONFIG_PATH`
   573  
   574  and the equivalent above commands would be
   575  
   576  ```sh
   577  go-oauth2-server --configBackend consul migrate
   578  ```
   579  ```sh
   580  go-oauth2-server --configBackend consul runserver
   581  ```
   582  
   583  ## Testing
   584  
   585  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.
   586  
   587  To run tests:
   588  
   589  ```sh
   590  make test
   591  ```
   592  
   593  ## Docker
   594  
   595  Build a Docker image and run the app in a container:
   596  
   597  ```sh
   598  docker build -t go-oauth2-server:latest .
   599  docker run -e ETCD_ENDPOINTS=localhost:2379 -p 8080:8080 --name go-oauth2-server go-oauth2-server:latest
   600  ```
   601  
   602  You can load fixtures with `docker exec` command:
   603  
   604  ```sh
   605  docker exec <container_id> /go/bin/go-oauth2-server loaddata \
   606    oauth/fixtures/scopes.yml \
   607    oauth/fixtures/roles.yml \
   608    oauth/fixtures/test_clients.yml
   609  ```
   610  
   611  ## Docker Compose
   612  
   613  You can use [docker-compose](https://docs.docker.com/compose/) to start the app, postgres, etcd in separate linked containers:
   614  
   615  ```sh
   616  docker-compose up
   617  ```
   618  
   619  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:
   620  
   621  ```sh
   622  curl --compressed -v localhost:8080/v1/health
   623  ```
   624  
   625  ## Supporting the project
   626  
   627  Donate BTC to my wallet if you find this project useful: `12iFVjQ5n3Qdmiai4Mp9EG93NSvDipyRKV`
   628  
   629  ![Donate BTC][5]