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