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 ```