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]