Commit 081bfdd1 authored by Yifan Gu's avatar Yifan Gu

Merge pull request #103 from yifan-gu/offline

return refresh token only when scope contains 'offline_access'
parents d87b5c9b fb72e607
...@@ -11,30 +11,22 @@ Sec. 2. [ID Token](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) ...@@ -11,30 +11,22 @@ Sec. 2. [ID Token](http://openid.net/specs/openid-connect-core-1_0.html#IDToken)
- None of the OPTIONAL claims (`acr`, `amr`, `azp`, `auth_time`) are supported - None of the OPTIONAL claims (`acr`, `amr`, `azp`, `auth_time`) are supported
- dex signs using JWS but does not do the OPTIONAL encryption. - dex signs using JWS but does not do the OPTIONAL encryption.
Sec. 3. [Authentication](http://openid.net/specs/openid-connect-core-1_0.html#Authentication) Sec. 3. [Authentication](http://openid.net/specs/openid-connect-core-1_0.html#Authentication)
- Only the authorization code flow (where `response_type` is `code`) is supported. - Only the authorization code flow (where `response_type` is `code`) is supported.
Sec. 3.1.2. [Authorization Endpoint](http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint) Sec. 3.1.2. [Authorization Endpoint](http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint)
- In a production system TLS is required but the dex web-server only supports HTTP right now - it is expected that until HTTPS is supported, TLS termination will be handled outside of dex. - In a production system TLS is required but the dex web-server only supports HTTP right now - it is expected that until HTTPS is supported, TLS termination will be handled outside of dex.
Sec. 3.1.2.1. [Authentication Request](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) Sec. 3.1.2.1. [Authentication Request](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)
- dex doesn't check the value of "scope" to make sure it contains "openid" in authentication requests.
- max_age not implemented; it's OPTIONAL in the spec, but if it's present servers MUST include auth_time, which dex does not. - max_age not implemented; it's OPTIONAL in the spec, but if it's present servers MUST include auth_time, which dex does not.
- None of the other OPTIONAL parameters are implemented with the exception of: - None of the other OPTIONAL parameters are implemented with the exception of:
- state - state
- nonce - nonce
- dex also defines a non-standard `register` parameter; when this parameter is `1`, end-users are taken through a registration flow, which after completing successfully, lands them at the specified `redirect_uri` - dex also defines a non-standard `register` parameter; when this parameter is `1`, end-users are taken through a registration flow, which after completing successfully, lands them at the specified `redirect_uri`
Sec. 3.1.2.2. [Authentication Request Validation](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation)
- As mentioned earlier, dex doesn't validate that the `openid` scope value is present.
Sec. 3.2.2.3. [Authorization Server Authenticates End-User](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthenticates) Sec. 3.2.2.3. [Authorization Server Authenticates End-User](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthenticates)
- The spec states that the authentication server "MUST NOT interact with the End-User" when `prompt` is `none` We don't check the prompt parameter at all; similarly, dex MUST re-prompt when `prompt` is `login` - dex does not do this either. - The spec states that the authentication server "MUST NOT interact with the End-User" when `prompt` is `none` We don't check the prompt parameter at all; similarly, dex MUST re-prompt when `prompt` is `login` - dex does not do this either.
Sec. 3.1.3.2. [Token Request Validation](http://openid.net/specs/openid-connect-core-1_0.html#TokenRequestValidation) Sec. 3.1.3.2. [Token Request Validation](http://openid.net/specs/openid-connect-core-1_0.html#TokenRequestValidation)
- In Token requests, dex chooses to proceed without error when `redirect_uri` is not present and there's only one registered valid URI (which is valid behavior) - In Token requests, dex chooses to proceed without error when `redirect_uri` is not present and there's only one registered valid URI (which is valid behavior)
...@@ -64,8 +56,8 @@ Sec. 9. [Client Authentication](http://openid.net/specs/openid-connect-core-1_0. ...@@ -64,8 +56,8 @@ Sec. 9. [Client Authentication](http://openid.net/specs/openid-connect-core-1_0.
- dex only supports the `client_secret_basic` client authentication type. - dex only supports the `client_secret_basic` client authentication type.
Sec. 11. [Offline Access](http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess) Sec. 11. [Offline Access](http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess)
- dex does not implement this feature. - offline_access in 'scope' is supported, but as we haven't implemented 'prompt' yet, the
spec's requirement is not fully met yet.
Sec. 15.1. [Mandatory to Implement Features for All OpenID Providers](http://openid.net/specs/openid-connect-core-1_0.html#ImplementationConsiderations) Sec. 15.1. [Mandatory to Implement Features for All OpenID Providers](http://openid.net/specs/openid-connect-core-1_0.html#ImplementationConsiderations)
- dex is missing the follow mandatory features (some are already noted elsewhere in this document): - dex is missing the follow mandatory features (some are already noted elsewhere in this document):
......
-- +migrate Up
ALTER TABLE session ADD COLUMN "scope" text;
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// db/migrations/0004_session_nonce.sql // db/migrations/0004_session_nonce.sql
// db/migrations/0005_refresh_token_create.sql // db/migrations/0005_refresh_token_create.sql
// db/migrations/0006_user_email_unique.sql // db/migrations/0006_user_email_unique.sql
// db/migrations/0007_session_scope.sql
// db/migrations/assets.go // db/migrations/assets.go
// DO NOT EDIT! // DO NOT EDIT!
...@@ -89,7 +90,7 @@ func dbMigrations0001_initial_migrationSql() (*asset, error) { ...@@ -89,7 +90,7 @@ func dbMigrations0001_initial_migrationSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0001_initial_migration.sql", size: 1388, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0001_initial_migration.sql", size: 1388, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -109,7 +110,7 @@ func dbMigrations0002_dex_adminSql() (*asset, error) { ...@@ -109,7 +110,7 @@ func dbMigrations0002_dex_adminSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0002_dex_admin.sql", size: 126, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0002_dex_admin.sql", size: 126, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -129,7 +130,7 @@ func dbMigrations0003_user_created_atSql() (*asset, error) { ...@@ -129,7 +130,7 @@ func dbMigrations0003_user_created_atSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0003_user_created_at.sql", size: 111, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0003_user_created_at.sql", size: 111, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -149,7 +150,7 @@ func dbMigrations0004_session_nonceSql() (*asset, error) { ...@@ -149,7 +150,7 @@ func dbMigrations0004_session_nonceSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0004_session_nonce.sql", size: 60, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0004_session_nonce.sql", size: 60, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -169,7 +170,7 @@ func dbMigrations0005_refresh_token_createSql() (*asset, error) { ...@@ -169,7 +170,7 @@ func dbMigrations0005_refresh_token_createSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0005_refresh_token_create.sql", size: 505, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0005_refresh_token_create.sql", size: 505, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -189,12 +190,32 @@ func dbMigrations0006_user_email_uniqueSql() (*asset, error) { ...@@ -189,12 +190,32 @@ func dbMigrations0006_user_email_uniqueSql() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/0006_user_email_unique.sql", size: 99, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/0006_user_email_unique.sql", size: 99, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _dbMigrationsAssetsGo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x99\x5d\x6f\xdb\x56\xd2\xc7\xaf\xad\x4f\xc1\x1a\x68\x21\x3d\xf0\x23\xf3\x55\x12\x0d\xe4\xa6\x49\x16\xc8\x45\x53\x60\x9b\xbd\x5a\x2f\x84\x43\xf2\xd0\x25\x2a\x4b\x8e\x28\x77\x9d\x04\xf9\xee\x3b\xbf\x33\x23\xdb\xb1\x25\x3b\x71\x1a\x04\xbd\x60\x2c\x1e\xce\x99\x33\xef\x33\xe7\x9f\xe3\xe3\xe8\xf9\xaa\xf1\xd1\x99\x5f\xfa\xb5\xdb\xf8\x26\xaa\xde\x45\x67\xab\xff\xaf\xba\x65\xe3\x36\x6e\x3c\x10\x82\x7e\x75\xb9\xae\x7d\x7f\xc2\xef\xa6\x3a\x3e\xef\xce\x84\xb2\x5b\x2d\xfb\xe3\x38\x8e\x93\x79\xb7\xec\x36\x9d\x5b\xcc\xaf\xd7\xc7\xfd\xdb\xc5\x4e\xda\x74\xde\xf8\xab\xb9\x6b\xce\xbb\xfd\x34\xd9\xfc\xb2\xf7\xeb\x79\xbd\xf6\x48\x33\x77\x9b\xbd\x94\xf9\xbc\xf7\x7d\x2f\x6f\xf3\xe5\x6a\x59\xfb\xbd\x74\xc5\x7c\xed\xdb\xb5\xef\x7f\x9f\x6f\x56\x7f\xf8\xa5\xb1\xde\x4b\x3e\x51\x01\xfc\xb9\xeb\x16\xf3\xcb\x65\xf7\xf6\x72\x0f\xad\xeb\x7b\xbf\xe9\xc7\x67\x2b\x3e\xbd\xf8\x35\x7a\xfd\xeb\x9b\xe8\xe5\x8b\x57\x6f\x7e\x18\x0c\x2e\x5c\xfd\x87\x3b\xf3\xd1\x0d\xf5\x60\xd0\x9d\x5f\xac\xd6\x9b\x68\x38\x38\x38\xac\xde\x6d\x7c\x7f\x28\x3f\xea\xd5\xf9\x85\x48\xd6\x1f\x9f\xbd\xef\x2e\x58\x68\xcf\x37\xfc\xe9\x56\xfc\xdb\x6f\xd6\xdd\xf2\x2c\x10\xae\xc2\xbf\x9b\xee\xdc\xeb\xe7\xe3\x6e\x75\xb9\xe9\x16\xbc\x5c\xb8\xcd\xef\xc7\x6d\xb7\xf0\xfc\x38\x1c\x8c\x06\x83\xf6\x72\x59\x47\xe6\xc0\x7f\x7a\xd7\x0c\xf9\x11\xfd\xfb\x3f\x1c\x7b\x14\x2d\xdd\xb9\x8f\x94\xf5\x28\x1a\x6e\x57\xfd\x7a\xbd\x5a\x8f\xa2\x0f\x83\x83\xb3\xf7\xe1\x2d\x3a\x79\x16\x21\xd5\xf8\xb5\xff\x2f\x4c\xfc\x7a\x18\xc4\xe6\xfd\xe7\xcb\xb6\x95\x77\xd8\x8e\x46\x83\x83\xae\x0d\x1b\x7e\x78\x16\x2d\xbb\x05\x2c\x0e\xd6\x7e\x73\xb9\x5e\xf2\x7a\x14\x89\x4a\xe3\x97\x70\x6f\x87\x87\x30\x8a\x7e\x7c\x7b\x12\xfd\xf8\xe7\xa1\x4a\x12\xce\x12\x1e\x1f\x07\x83\x83\x3f\xdd\x3a\xaa\x2e\xdb\x48\xcf\xd1\x43\x06\x07\x73\x15\xe7\x59\xd4\xad\xc6\xcf\x57\x17\xef\x86\x3f\x09\xcd\x91\xc8\x26\xbb\xea\xc5\xcb\xad\xa4\xe3\xe7\x8b\x55\xef\x87\xa2\xfe\x5f\x24\x0f\x6c\x94\xff\x1e\x46\x42\xa8\x72\xdb\xa2\x88\x35\xfe\x19\xd1\x87\xa3\x23\x28\x06\xf2\x6d\xf3\xee\xc2\x47\x21\x50\x30\xf9\x65\xbd\x81\x4b\xd0\xcf\xfc\x21\xc7\x2c\xdb\x55\x14\xad\xfa\xf1\x3f\xc4\x87\xaf\xe4\xe5\x7a\x9f\xb9\x70\xbb\x7e\x8b\xc3\x2d\x1f\x0e\x0e\xfa\xee\xbd\x8f\xba\xe5\x66\x92\x0f\x0e\xce\xc9\x65\xe3\xf5\x8b\xfc\x0e\x2b\x6f\x24\x6c\x22\x62\x67\xcc\x2f\xd8\x87\x08\x19\xb6\xdd\xdd\x23\x46\xd1\x6b\xe1\x3c\x1c\x19\x6f\x8e\x32\xe5\xda\x6e\xcc\xa1\xb2\x79\xff\xde\xdf\x44\x10\xd9\x1b\x44\xf9\x74\x2b\x22\x3e\xb8\x15\x59\x65\xeb\x2d\xc9\x3f\x65\x80\x5e\x8f\x31\x40\x39\xe1\x71\xad\xe8\x3d\x0e\xa6\xfd\x7e\x26\xaf\xfa\x17\xdd\x5a\x58\x54\xab\xd5\xe2\xf6\x6e\xb7\xe8\x1f\xd1\xfc\x5d\xaf\x8a\xfb\x75\xeb\x6a\xff\xe1\xe3\xad\xdd\x16\x09\x04\xf7\xbc\xa9\x7e\xb9\xae\x08\xbb\x6b\xe7\x6f\x6f\x17\x12\xea\x1a\x1b\xc3\xc3\xd3\xab\xa4\x3d\xbd\x9a\x55\xa7\x57\xf1\x4c\x9e\xd8\x9e\xf2\xf4\x6a\xe2\x65\xdd\xd6\x5a\xa1\x29\x6b\x79\xb2\xd3\xab\x1a\x7a\x77\x7a\xd5\xc8\x9e\x4c\xbe\x25\xf2\xd4\x93\xd3\x2b\x2f\xeb\x53\xd9\x17\xcb\xb7\x32\x91\xef\x42\x3b\x93\xf5\x89\x3c\xa5\x7c\x73\x42\xe7\xa6\xf2\xde\x08\x9d\x7c\x9f\xc8\xe3\xe4\xa9\x72\xa1\x95\x6f\xd3\x42\xdf\x33\xa1\xc9\x58\x97\xf7\x14\xbe\x22\x47\x81\x0c\xb2\x2f\x17\x9e\x89\xf0\x9f\x08\xbf\x66\xaa\x7f\x0b\x7e\xcb\xb9\xb9\xd0\x25\xc2\xab\x96\xf5\xda\xab\x4c\xec\x9f\x09\xaf\x99\xac\x17\xa2\x4b\x23\xef\xad\xe8\xe1\x91\xa9\xd2\xfd\xc8\x97\x78\xb5\x43\x2d\x67\xc6\x53\x3d\x07\x79\xd0\x3d\x67\x8f\xe9\x03\x7d\xe6\xd5\x1e\xa9\xf0\x2b\x39\x47\xe4\xc9\x53\xf9\x2d\x67\xe4\xad\xea\x5a\xc1\x0f\xd9\x65\xbd\x2d\xd5\xbe\xad\xd0\xb7\xb2\x56\x35\x26\x27\xfa\x0a\xad\x97\xf3\x4a\x79\x5a\xa1\xcd\x65\x6d\x52\xab\x1d\x3c\xb6\x90\xef\xb9\xf0\xcf\x90\x51\xde\x6b\x91\xa1\x90\xdf\x59\xad\x74\xb5\xf0\x69\x63\xd5\xbf\x94\xfd\x8d\x53\xfb\xa3\x3b\x76\x44\x06\x74\x76\x85\xca\xee\x32\xb5\x2b\x7b\xb0\x4b\x5c\x9b\x6d\xcc\x2e\xc8\x34\xdd\xfa\xab\x55\x39\x62\x74\x4f\xf5\xec\x5a\x64\xaf\x4b\x5d\x9f\x1a\x7d\x1e\xab\xcd\xea\xd4\xe4\x91\xef\xbe\xd1\x78\xc1\x76\x55\xac\x3a\xc6\x95\xc6\x8d\x4b\xd5\x77\xf8\x1c\xdd\x58\x8f\x27\x1a\x1b\x75\xa2\x71\x82\x5c\xe8\x94\xc8\xdf\x0a\x9f\x67\xea\x9f\x10\x1b\xec\x95\x73\x0b\xec\xe6\x94\x4f\x25\x3c\x53\x39\xbb\x44\x7e\xe2\xb6\xb4\xf8\xac\xf4\x0c\xe2\x18\xff\xc7\xf2\xdd\xe7\xea\xab\x8a\x18\x2c\x54\xff\x36\xd3\xf7\x3c\x51\x59\xa0\x43\x86\x18\x9f\xf3\x6d\xaa\xfc\x83\xfd\xcd\x9f\x89\xd0\x54\xf2\x37\x8d\x35\x36\xaa\x56\xe9\x91\x6d\x2a\xf4\x5e\xce\xf3\xb1\xda\x1f\x7b\xd6\xb5\xae\x91\x23\x59\xa3\x3e\x21\x97\x88\x07\x64\xe2\x6c\x6c\x38\xab\x4d\x1e\x59\x9f\xa5\x9a\x87\xf8\x0b\xf9\xa1\x87\x3f\xb6\x45\x86\x60\xfb\x56\xf3\xa5\x15\xba\x69\xa6\x7c\xf2\x52\x6d\x12\x9b\xbd\xf0\x03\x32\x15\xf2\x3d\x4d\xd5\x76\xc8\x05\x3d\x67\xe4\xc2\x2f\xa9\x94\x2f\x71\x47\xac\x92\x23\x29\x75\x20\xd6\xef\xd8\xd1\x95\x9a\x53\xe8\x5d\xd4\x9a\x3b\xf8\x3c\x99\x69\x5c\xe0\xb3\x29\xdf\x9c\xea\x3b\x29\x55\x3e\x47\x3c\xca\x5a\xb1\xf5\x7d\xa6\xbc\x88\xd9\x5a\xf8\xa7\x53\xb5\x39\xf1\x49\xed\x21\x1f\x1d\x71\x49\xad\x68\x34\x3f\xa0\xc1\x16\xf8\x87\xb8\x28\xbc\xca\x90\x37\x1a\xe7\xd4\x88\xb4\x50\x79\x89\xcb\xa0\x7f\xa9\xe7\x62\x03\xe2\x92\xbc\xa1\xde\x10\x4f\xd0\x87\x73\x26\xea\x13\x64\x09\x75\x50\xd6\x7c\xad\xb1\x33\x8d\x95\x3f\xf5\x0c\x9d\x9b\x46\x65\xe2\x37\x36\xcf\xa7\x6a\x37\xcf\xb9\xc2\x77\x22\x67\xb5\x4e\x6b\x50\x41\xac\x41\x97\x6a\x8c\xe1\x3b\x7c\x0e\x0f\xd6\x52\xf8\x96\x9a\x0f\xd4\x43\xfc\x4d\xde\xce\xac\x9e\xc2\x1f\x1f\x38\xe8\x26\xaa\x77\x55\xab\x1d\xb0\x31\xf1\xca\x19\xf8\x90\xba\x8c\xbe\xc4\x49\x8b\x7d\x6b\xad\x93\xd4\x30\x9f\x69\xfc\x4f\xa9\x59\xad\xea\x54\x10\xff\xf8\x4a\xd6\xab\x99\xfa\x97\xba\x10\x7a\xc1\x54\x6b\x79\xe5\xd4\xe7\xc4\x15\x71\xb4\xed\x0d\xd4\x3e\x1e\xe7\xb5\xce\x91\x43\xd4\x72\xea\x51\x5c\x6c\xe9\x0e\xb7\x53\xe2\x67\x35\x24\x1b\x6a\x76\x0d\x8b\xdb\xd1\xe7\xd6\xb0\x29\x53\xd2\xe7\xf5\xb9\x23\xa1\x3c\xfc\xdc\xfb\xc4\xa1\x50\x8f\xae\x47\x97\xcf\xe2\x8f\xc4\xff\x17\x66\xaf\xdb\x12\x87\xe1\xeb\x7a\xc2\xfd\x12\xfd\x1f\x1b\x2c\xaf\xe7\xc1\x30\xd1\x09\xf3\x3b\x63\xc2\x07\x06\xa8\x93\xe8\x0b\x54\x8e\x98\x9b\x4e\xa2\x24\x9b\xcd\x8e\x22\x46\xa0\x93\xdb\x13\xd2\x30\x4f\xe3\x51\x58\x67\xb0\x39\xd1\xc1\xe7\x5f\xcb\xee\x6a\x98\x1c\x45\xf1\x48\x26\x58\x87\x14\x3f\x05\x13\x7c\x08\x7a\x9f\x44\xa6\x3e\x22\x9e\x44\xe1\xcf\xc7\x6b\x2f\xba\xa3\x87\x86\x95\x5b\x97\xb7\xa7\x0e\x29\x34\xaa\xd0\x1c\x0a\x6d\x0c\x61\x10\x68\x34\xc0\x53\x6b\xa6\x69\xad\x05\x12\x1a\xf8\x91\x88\x14\xea\x29\x09\x94\x2b\x5f\x12\x32\x6e\xb4\x60\x41\xc3\xfb\x34\xd7\x26\x3d\xb5\xc4\xcb\x67\xc6\xbf\x54\xfe\x9c\x93\x56\xda\x7c\x48\x76\xd6\x68\x9e\x14\x16\x12\x9d\xe2\x44\x83\xa2\x41\xcf\x64\x5f\x6a\x4f\x68\xd8\x5e\x13\x8d\xe1\x22\x0c\x45\x5e\x1b\x59\x6c\x49\x48\x21\xa6\x60\xd2\x74\x18\x46\x38\x83\x35\x92\x18\xde\x14\xb0\xdc\x9a\x3f\xba\xb5\xf6\x64\xb6\x6f\x66\x34\x14\x7d\x8a\x66\x51\xdf\x34\x77\x64\x08\xf2\x24\x9a\xd4\xdb\x33\x27\x33\x2d\x22\xc8\xcc\xc0\x42\xa3\x28\x6c\x08\xa3\x78\x86\xc1\x0d\xfb\x39\x2d\xd6\xe8\x47\xc1\x82\x96\x66\x4e\x13\xa0\x38\x71\x46\x9c\xde\x2f\x20\xc8\x54\x59\xe3\x0d\x85\xde\xdf\xf2\xed\xde\x02\xf2\x69\x90\x7c\x75\xe1\xf8\x94\xdd\xee\x82\x71\x07\x54\x78\xb0\x50\x7c\xca\xef\x09\x05\x62\xa7\x7e\xdf\xac\x30\xdc\x53\x6d\x5b\x10\xd2\xc9\xf7\xaf\x07\xf7\x80\x9a\xbf\x4b\x55\x80\x57\x6a\xa3\x32\xef\x21\x5b\x5b\x5d\xe3\x9a\x92\x1a\x0f\x5a\x31\xfb\xc3\xde\x44\xdf\xb9\x22\xd0\xc2\x39\xaf\x2d\xf4\x29\x2c\xe3\x82\x9c\xde\x32\xbd\xd0\xcc\xcb\x6d\xdc\xe5\xea\xc1\xb9\xf0\x81\x77\xa8\x4e\xce\x2a\x93\x8d\x9f\xa9\x8d\x0d\xde\xae\x26\x64\x3a\xfa\x04\x39\xec\x3a\xc0\xf8\xd8\xc8\x93\xa4\x3a\x06\x51\x01\xc8\x7c\xe7\xac\x7a\x4c\x74\x4c\x67\x6c\x2b\x6d\x7c\x4f\xe2\xfb\xd9\x9d\x5a\x75\xf4\x76\x95\xe1\xfa\xf1\x78\x76\xef\x72\xf9\x57\xe7\xf8\x2e\xa6\xbb\x33\x7d\x27\x34\xf8\x60\xbe\xef\xe2\xfd\x84\xac\x7f\x40\xef\x6f\x96\xfb\x7b\x94\xdd\x56\x80\x24\xf9\xfe\x15\xe0\x0e\x00\xfb\x77\xc8\x7f\xe2\x7e\x9b\xe3\xe4\x6a\x38\xcb\x72\x90\x3c\xfe\xd2\x9c\x27\x87\xeb\xca\x26\x8a\x4a\x79\x73\x0e\x32\x84\xb3\x4a\xbd\x22\xf0\x7b\x9b\xdb\xa1\xdb\xe6\xf7\x73\x92\x2b\x20\x30\x02\xd7\x25\xec\xc6\x75\xe8\xf1\x9c\xbc\xef\x84\xaf\xce\xc8\xfb\x2c\x77\xe7\xe3\x0e\x00\xfe\xc1\x6c\xbc\xcf\xf7\x09\xb9\xb8\x57\xdf\x6f\x96\x89\x3b\xd5\xb4\x3c\x9c\xc4\xdf\x3f\x0d\x77\xff\xff\xc6\x53\xb3\x91\xcb\x70\x19\x6b\xf4\xd3\xb5\x00\x3d\xae\x81\x44\x2e\xaf\xad\x66\x03\xf3\x2d\xe0\x1f\x19\xc7\xfe\x49\xaa\xb4\x74\x31\xe6\x64\xe6\x52\x80\xa6\xd8\xc0\x10\x66\xc8\x30\x77\xda\x1c\x4e\x56\x32\xa3\x16\x06\x70\x01\xdc\x00\xe0\x00\x1e\x30\x97\x92\x35\x01\xa0\xf2\x7a\xf9\xe6\x22\x0b\x30\x80\xfc\x80\x1a\xcc\xbf\x41\x16\xa7\x17\xfe\xb6\xd2\xf7\xd4\x32\x89\x8b\x35\x97\x74\x66\xe9\xca\x2e\xf3\x01\xc4\x34\x20\x85\x0b\x3b\x80\x08\x99\x98\x1a\x58\x00\xf8\x12\xba\x65\xae\xf4\x45\x72\x03\xc0\x01\x20\x55\x13\x05\xf1\xb8\xf0\x57\x06\x62\x61\x3f\x2a\x06\x76\x40\xfe\x60\x47\x00\x8a\x4c\xa7\x00\x40\x81\x72\xa6\x17\xf8\x70\x47\x68\x14\x74\x2b\xac\x02\x00\x0e\x00\x78\x01\x98\x20\x77\x65\x53\x02\x3a\x01\x58\x38\x03\xe6\xc2\x7a\xae\x32\xc4\x36\xc3\x23\x13\xfa\xcd\x5a\x05\x23\x5d\x6d\xf2\x64\x0a\x6a\x71\xbf\x28\xad\xf2\x61\x3f\xe4\x0c\x3e\xf1\x5a\x51\x01\x86\x00\x3c\x02\xa8\x36\x55\x40\x08\x50\x18\x7e\x4c\x18\x85\x81\x10\x01\x74\xe2\x7e\xd4\x98\xde\x85\x82\x10\xf8\x32\x80\x9f\x99\xda\x89\x69\xa3\xd8\xfa\x27\xd5\x73\xd0\x8f\xbf\xac\x05\xd0\x69\xaa\xf4\x00\x32\x54\xc2\x00\x52\x55\x06\xf0\xe6\x37\xd3\x11\xfb\xa9\xbc\x39\xba\x1a\x4f\xbf\xd5\xab\xd6\xbf\xad\xdd\x8b\x88\xe3\x00\x62\xce\x94\x37\xe0\x28\xf7\x1d\xec\x95\x6e\xbb\x4c\xa5\x13\x95\x2f\x0d\xb8\x33\x70\x9b\x73\xb9\xdb\x14\x06\xfe\x12\x33\x00\x54\xfc\x6d\x0c\x30\x66\x92\xc3\x7e\x74\x1a\x67\xc0\x1d\x71\x47\xf7\xc1\xb7\x85\xdd\x21\x2b\x03\xc2\x89\x2b\x40\x24\xee\x5a\xc4\x13\xf6\x4b\x0c\xa8\x42\x26\x80\x41\xfc\x00\xd0\x87\x6c\xac\x05\x60\x32\x56\x90\x08\xf0\x1b\x5f\x91\x77\xc1\xe6\x80\x60\x4e\xe5\x63\xd2\x0b\x00\xa9\x01\x72\x00\x47\x99\x01\x57\xa1\xb3\xe5\x37\xf1\x12\xf2\xb2\x50\x7d\xf1\x43\x63\x39\xc3\x74\x09\x40\x4c\x0e\x71\xc7\x63\xa2\x0c\x80\x9a\x81\xa2\x74\x3f\x40\x73\x80\xc6\x30\x69\x7a\xeb\xae\xa5\xdd\x2f\x33\xfd\x16\x62\xa1\xb5\x3b\xe4\x9d\x6e\xc6\x13\x00\xb6\xe6\x06\x78\xbe\xa1\xdb\xd7\xcd\xf6\xd6\xb2\xaf\x6e\x6a\x7b\x39\xef\xee\x6d\xfb\xff\xd3\xf8\xc1\x16\xb7\xf7\x94\x27\x74\xba\xc7\x6c\xf1\x97\x34\xbc\xff\x05\x00\x00\xff\xff\x4f\xed\x5c\x50\x00\x20\x00\x00") var _dbMigrations0007_session_scopeSql = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd2\xd5\x55\xd0\xce\xcd\x4c\x2f\x4a\x2c\x49\x55\x08\x2d\xe0\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x4e\x2d\x2e\xce\xcc\xcf\x53\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x50\x2a\x4e\xce\x2f\x48\x55\x52\x28\x49\xad\x28\xb1\xe6\x02\x04\x00\x00\xff\xff\x79\x7f\x20\x70\x3c\x00\x00\x00")
func dbMigrations0007_session_scopeSqlBytes() ([]byte, error) {
return bindataRead(
_dbMigrations0007_session_scopeSql,
"db/migrations/0007_session_scope.sql",
)
}
func dbMigrations0007_session_scopeSql() (*asset, error) {
bytes, err := dbMigrations0007_session_scopeSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "db/migrations/0007_session_scope.sql", size: 60, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _dbMigrationsAssetsGo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x99\xdf\x6f\xdb\x46\x12\xc7\x9f\xad\xbf\x82\x35\xd0\x42\x3e\xf8\x64\xfe\x14\x45\x03\x7d\x69\x93\x03\xf2\xd0\x14\xb8\xe6\x9e\xce\x07\x61\x49\x2e\x5d\xa2\xb2\xe4\x88\x72\xcf\x49\x90\xff\xfd\xe6\xb3\x33\xb2\x9d\x58\xb2\x13\xe7\x82\xa0\x0f\x8c\xc5\xe5\xec\xec\xfc\x9e\xd9\x6f\x4e\x4e\xa2\x9f\x57\xad\x8f\xce\xfd\xd2\xaf\xdd\xc6\xb7\x51\xfd\x26\x3a\x5f\xfd\xbd\xee\x97\xad\xdb\xb8\xc9\x48\x08\x86\xd5\xd5\xba\xf1\xc3\x29\xbf\xdb\xfa\xe4\xa2\x3f\x17\xca\x7e\xb5\x1c\x4e\xe2\x38\x4e\xe6\xfd\xb2\xdf\xf4\x6e\x31\xbf\x59\x9f\x0c\xaf\x17\x3b\x69\xd3\x79\xeb\xaf\xe7\xae\xbd\xe8\xf7\xd3\x64\xf3\xab\xc1\xaf\xe7\xcd\xda\x23\xcd\xdc\x6d\xf6\x52\xe6\xf3\xc1\x0f\x83\xbc\xcd\x97\xab\x65\xe3\xf7\xd2\x15\xf3\xb5\xef\xd6\x7e\xf8\x7d\xbe\x59\xfd\xe1\x97\xc6\x7a\x2f\xf9\x54\x05\xf0\x17\xae\x5f\xcc\xaf\x96\xfd\xeb\xab\xfd\xb4\xe5\x8d\x08\x43\xb3\xba\xdc\x43\xe7\x86\xc1\x6f\x86\xc9\xf9\x8a\x4f\xcf\x7e\x8d\x5e\xfe\xfa\x2a\x7a\xfe\xec\xc5\xab\xef\x46\xa3\x4b\xd7\xfc\xe1\xce\x7d\x74\x4b\x3d\x1a\xf5\x17\x97\xab\xf5\x26\x1a\x8f\x0e\x0e\xeb\x37\x1b\x3f\x1c\xca\x8f\x66\x75\x71\x29\x1a\x0c\x27\xe7\x6f\xfb\x4b\x16\xba\x8b\x0d\x7f\xfa\x15\xff\x0e\x9b\x75\xbf\x3c\x0f\x84\xab\xf0\xef\xa6\xbf\xf0\xfa\xf9\xa4\x5f\x5d\x6d\xfa\x05\x2f\x97\x6e\xf3\xfb\x49\xd7\x2f\x3c\x3f\x0e\x47\x47\xa3\x51\x77\xb5\x6c\x22\x73\xf4\x3f\xbd\x6b\xc7\xfc\x88\xfe\xfd\x1f\x8e\x3d\x8e\x96\xee\xc2\x47\xca\xfa\x28\x1a\x6f\x57\xfd\x7a\xbd\x5a\x1f\x45\xef\x46\x07\xe7\x6f\xc3\x5b\x74\xfa\x63\x84\x54\x93\x97\xfe\xbf\x30\xf1\xeb\x71\x10\x9b\xf7\x9f\xae\xba\x4e\xde\x61\x7b\x74\x34\x3a\xe8\xbb\xb0\xe1\xbb\x1f\xa3\x65\xbf\x80\xc5\xc1\xda\x6f\xae\xd6\x4b\x5e\x8f\x23\x51\x69\xf2\x1c\xee\xdd\xf8\x10\x46\xd1\xf7\xaf\x4f\xa3\xef\xff\x3c\x54\x49\xc2\x59\xc2\xe3\xfd\x68\x74\xf0\xa7\x5b\x47\xf5\x55\x17\xe9\x39\x7a\xc8\xe8\x60\xae\xe2\xfc\x18\xf5\xab\xc9\xcf\xab\xcb\x37\xe3\x1f\x84\xe6\x58\x64\x93\x5d\xcd\xe2\xf9\x56\xd2\xc9\xcf\x8b\xd5\xe0\xc7\xa2\xfe\xff\x49\x1e\xd8\x28\xff\x3d\x8c\x84\x50\xe5\xb6\x45\x11\x6b\xf2\x13\xa2\x8f\x8f\x8e\xa1\x18\xc9\xb7\xcd\x9b\x4b\x1f\x85\x40\xc1\xe4\x57\xcd\x06\x2e\x41\x3f\xf3\x87\x1c\xb3\xec\x56\x51\xb4\x1a\x26\xff\x10\x1f\xbe\x90\x97\x9b\x7d\xe6\xc2\xed\xfa\x1d\x0e\x77\x7c\x38\x3a\x18\xfa\xb7\x3e\xea\x97\x9b\x69\x3e\x3a\xb8\x20\xe7\x8d\xd7\x2f\xf2\x3b\xac\xbc\x92\xb0\x89\x88\x9d\x09\xbf\x60\x1f\x22\x64\xdc\xf5\x1f\x1f\x71\x14\xbd\x14\xce\xe3\x23\xe3\xcd\x51\xa6\x5c\xd7\x4f\x38\x54\x36\xef\xdf\xfb\x9b\x08\x22\x7b\x83\x28\x1f\x6e\x45\xc4\x07\xb7\x22\xab\x6c\xbd\x23\xf9\x87\x0c\xd0\xeb\x31\x06\x28\x27\x3c\x6e\x14\xbd\xc7\xc1\xb4\xdf\xcf\xe4\xc5\xf0\xac\x5f\x0b\x8b\x7a\xb5\x5a\xdc\xdd\xed\x16\xc3\x23\x9a\xbf\x19\x54\x71\xbf\xee\x5c\xe3\xdf\xbd\xbf\xb3\xdb\x22\x81\xe0\x9e\xb7\xf5\x2f\x37\x15\x61\x77\x8d\xfd\xed\xf5\x42\x42\x5d\x63\x63\x7c\x78\x76\x9d\x74\x67\xd7\xb3\xfa\xec\x3a\x9e\xc9\x13\xdb\x53\x9d\x5d\x4f\xbd\xac\xdb\x5a\x27\x34\x55\x23\x4f\x76\x76\xdd\x40\xef\xce\xae\x5b\xd9\x93\xc9\xb7\x44\x9e\x66\x7a\x76\xed\x65\xbd\x94\x7d\xb1\x7c\xab\x12\xf9\x2e\xb4\x33\x59\x9f\xca\x53\xc9\x37\x27\x74\xae\x94\xf7\x56\xe8\xe4\xfb\x54\x1e\x27\x4f\x9d\x0b\xad\x7c\x2b\x0b\x7d\xcf\x84\x26\x63\x5d\xde\x53\xf8\x8a\x1c\x05\x32\xc8\xbe\x5c\x78\x26\xc2\x7f\x2a\xfc\xda\x52\xff\x16\xfc\x96\x73\x73\xa1\x4b\x84\x57\x23\xeb\x8d\x57\x99\xd8\x3f\x13\x5e\x33\x59\x2f\x44\x97\x56\xde\x3b\xd1\xc3\x23\x53\xad\xfb\x91\x2f\xf1\x6a\x87\x46\xce\x8c\x4b\x3d\x07\x79\xd0\x3d\x67\x8f\xe9\x03\x7d\xe6\xd5\x1e\xa9\xf0\xab\x38\x47\xe4\xc9\x53\xf9\x2d\x67\xe4\x9d\xea\x5a\xc3\x0f\xd9\x65\xbd\xab\xd4\xbe\x9d\xd0\x77\xb2\x56\xb7\x26\x27\xfa\x0a\xad\x97\xf3\x2a\x79\x3a\xa1\xcd\x65\x6d\xda\xa8\x1d\x3c\xb6\x90\xef\xb9\xf0\xcf\x90\x51\xde\x1b\x91\xa1\x90\xdf\x59\xa3\x74\x8d\xf0\xe9\x62\xd5\xbf\x92\xfd\xad\x53\xfb\xa3\x3b\x76\x44\x06\x74\x76\x85\xca\xee\x32\xb5\x2b\x7b\xb0\x4b\xdc\x98\x6d\xcc\x2e\xc8\x54\x6e\xfd\xd5\xa9\x1c\x31\xba\xa7\x7a\x76\x23\xb2\x37\x95\xae\x97\x46\x9f\xc7\x6a\xb3\x26\x35\x79\xe4\xbb\x6f\x35\x5e\xb0\x5d\x1d\xab\x8e\x71\xad\x71\xe3\x52\xf5\x1d\x3e\x47\x37\xd6\xe3\xa9\xc6\x46\x93\x68\x9c\x20\x17\x3a\x25\xf2\xb7\xc6\xe7\x99\xfa\x27\xc4\x06\x7b\xe5\xdc\x02\xbb\x39\xe5\x53\x0b\xcf\x54\xce\xae\x90\x9f\xb8\xad\x2c\x3e\x6b\x3d\x83\x38\xc6\xff\xb1\x7c\xf7\xb9\xfa\xaa\x26\x06\x0b\xd5\xbf\xcb\xf4\x3d\x4f\x54\x16\xe8\x90\x21\xc6\xe7\x7c\x2b\x95\x7f\xb0\xbf\xf9\x33\x11\x9a\x5a\xfe\xa6\xb1\xc6\x46\xdd\x29\x3d\xb2\x95\x42\xef\xe5\x3c\x1f\xab\xfd\xb1\x67\xd3\xe8\x1a\x39\x92\xb5\xea\x13\x72\x89\x78\x40\x26\xce\xc6\x86\xb3\xc6\xe4\x91\xf5\x59\xaa\x79\x88\xbf\x90\x1f\x7a\xf8\x63\x5b\x64\x08\xb6\xef\x34\x5f\x3a\xa1\x2b\x33\xe5\x93\x57\x6a\x93\xd8\xec\x85\x1f\x90\xa9\x90\xef\x69\xaa\xb6\x43\x2e\xe8\x39\x23\x17\x7e\x49\xad\x7c\x89\x3b\x62\x95\x1c\x49\xa9\x03\xb1\x7e\xc7\x8e\xae\xd2\x9c\x42\xef\xa2\xd1\xdc\xc1\xe7\xc9\x4c\xe3\x02\x9f\x95\x7c\x73\xaa\xef\xb4\x52\xf9\x1c\xf1\x28\x6b\xc5\xd6\xf7\x99\xf2\x22\x66\x1b\xe1\x9f\x96\x6a\x73\xe2\x93\xda\x43\x3e\x3a\xe2\x92\x5a\xd1\x6a\x7e\x40\x83\x2d\xf0\x0f\x71\x51\x78\x95\x21\x6f\x35\xce\xa9\x11\x69\xa1\xf2\x12\x97\x41\xff\x4a\xcf\xc5\x06\xc4\x25\x79\x43\xbd\x21\x9e\xa0\x0f\xe7\x4c\xd5\x27\xc8\x12\xea\xa0\xac\xf9\x46\x63\xa7\x8c\x95\x3f\xf5\x0c\x9d\xdb\x56\x65\xe2\x37\x36\xcf\x4b\xb5\x9b\xe7\x5c\xe1\x3b\x95\xb3\x3a\xa7\x35\xa8\x20\xd6\xa0\x4b\x35\xc6\xf0\x1d\x3e\x87\x07\x6b\x29\x7c\x2b\xcd\x07\xea\x21\xfe\x26\x6f\x67\x56\x4f\xe1\x8f\x0f\x1c\x74\x53\xd5\xbb\x6e\xd4\x0e\xd8\x98\x78\xe5\x0c\x7c\x48\x5d\x46\x5f\xe2\xa4\xc3\xbe\x8d\xd6\x49\x6a\x98\xcf\x34\xfe\x4b\x6a\x56\xa7\x3a\x15\xc4\x3f\xbe\x92\xf5\x7a\xa6\xfe\xa5\x2e\x84\x5e\x50\x6a\x2d\xaf\x9d\xfa\x9c\xb8\x22\x8e\xb6\xbd\x81\xda\xc7\xe3\xbc\xd6\x39\x72\x88\x5a\x4e\x3d\x8a\x8b\x2d\xdd\xe1\x76\x4a\xfc\xa4\x86\x64\x43\xcd\xae\x61\x71\x3b\xfa\xdc\x19\x36\x65\x4a\xfa\xb4\x3e\x77\x2c\x94\x87\x9f\x7a\xef\x38\x14\xea\xa3\x9b\xd1\xe5\x93\xf8\x23\xf1\xdf\xc2\xec\x75\x57\xe2\x30\x7c\xdd\x4c\xb8\x9f\xa3\xff\x63\x83\xe5\xcd\x3c\x18\x26\x3a\x61\xfe\xd1\x98\xf0\x8e\x01\xea\x34\xfa\x0c\x95\x23\xe6\xa6\xd3\x28\xc9\x66\xb3\xe3\x88\x11\xe8\xf4\xee\x84\x34\xce\xb3\xe9\x51\x58\x67\xb0\x39\xd5\xc1\xe7\x5f\xcb\xfe\x7a\x9c\x1c\x47\xf1\x91\x4c\xb0\x0e\x29\x7e\x08\x26\x78\x17\xf4\x3e\x8d\x4c\x7d\x44\x3c\x8d\xc2\x9f\xf7\x37\x5e\x74\xc7\x0f\x0d\x2b\x77\x2e\x79\x4f\x1d\x52\x68\x54\xa1\x39\x14\xda\x18\xc2\x20\xd0\x6a\x80\xa7\xd6\x4c\xd3\x46\x0b\x24\x34\xf0\x23\x11\x29\xd4\x25\x09\x94\x2b\x5f\x12\x32\x6e\xb5\x60\x41\xc3\x7b\x99\x6b\x93\x2e\x2d\xf1\xf2\x99\xf1\xaf\x94\x3f\xe7\xa4\xb5\x36\x1f\x92\x9d\x35\x9a\x27\x85\x85\x44\xa7\x38\xd1\xa0\x68\xd0\x33\xd9\x97\xda\x13\x1a\xb6\xd7\x44\x63\xb8\x08\x43\x91\xd7\x46\x16\x5b\x12\x52\x88\x29\x98\x34\x1d\x86\x11\xce\x60\x8d\x24\x86\x37\x05\x2c\xb7\xe6\x8f\x6e\x9d\x3d\x99\xed\x9b\x19\x0d\x45\x9f\xa2\x59\x34\xb7\xcd\x1d\x19\x82\x3c\x89\x26\xf5\xf6\xcc\xe9\x4c\x8b\x08\x32\x33\xb0\xd0\x28\x0a\x1b\xc2\x28\x9e\x61\x70\xc3\x7e\x4e\x8b\x35\xfa\x51\xb0\xa0\xa5\x99\xd3\x04\x28\x4e\x9c\x11\xa7\xf7\x0b\x08\x32\xd5\xd6\x78\x43\xa1\xf7\x77\x7c\xbb\xb7\x80\x7c\x18\x24\x5f\x5c\x38\x3e\x64\xb7\xbb\x60\x7c\x04\x3e\x3c\x58\x28\x3e\xe4\xf7\x84\x02\xb1\x53\xbf\xaf\x56\x18\xee\xa9\xb6\x2d\x08\xe9\xf4\xdb\xd7\x83\x7b\x80\xce\x5f\xa5\x2a\xc0\x2b\xb5\x51\x99\xf7\x90\xad\x9d\xae\x71\x4d\x49\x8d\x07\xad\x98\xfd\x61\x6f\xa2\xef\x5c\x11\x68\xe1\x9c\xd7\x15\xfa\x14\x96\x71\x41\x4e\x6f\x99\x5e\x68\xe6\xe5\x36\xee\x72\xf5\xe0\x5c\xf8\xc0\x3b\x54\x27\x67\x95\xc9\xc6\xcf\xd4\xc6\x06\x6f\x57\x13\x32\x1d\x7d\x82\x1c\x76\x1d\x60\x7c\x6c\xe5\x49\x52\x1d\x83\xa8\x00\x64\xbe\x73\x56\x3d\xa6\x3a\xa6\x33\xb6\x55\x36\xbe\x27\xf1\xfd\xec\x4e\xad\x3a\x7a\xbb\xca\x70\xfd\x78\x3c\xbb\x77\xb9\xfc\x8b\x73\x7c\x17\xd3\xdd\x99\xbe\x13\x42\x7c\x30\xdf\x77\xf1\x7e\x42\xd6\x3f\xa0\xf7\x57\xcb\xfd\x3d\xca\x6e\x2b\x40\x92\x7c\xfb\x0a\xf0\x11\x50\xfb\x57\xc8\x7f\xe2\x7e\x9b\xe3\xe4\x6a\x38\xcb\x72\x90\x3c\xfe\xdc\x9c\x27\x87\x9b\xda\x26\x8a\x5a\x79\x73\x0e\x32\x84\xb3\x2a\xbd\x22\xf0\x7b\x9b\xdb\xa1\xdb\xe6\xf7\x73\x92\x2b\x20\x30\x02\xd7\x25\xec\xc6\x75\xe8\xf1\x9c\xbc\xef\x84\x2f\xce\xc8\xfb\x2c\x77\xe7\xe3\x0e\xa0\xfe\xc1\x6c\xbc\xcf\xf7\x09\xb9\xb8\x57\xdf\xaf\x96\x89\x3b\xd5\xb4\x3c\x9c\xc6\xdf\x3e\x0d\x77\xff\x3f\xc8\x53\xb3\x91\xcb\x70\x15\x6b\xf4\xd3\xb5\x00\x3d\x6e\x80\x44\x2e\xaf\x9d\x66\x03\xf3\x2d\xe0\x1f\x19\xc7\xfe\x69\xaa\xb4\x74\x31\xe6\x64\xe6\x52\x80\xa6\xd8\xc0\x10\x66\xc8\x30\x77\xda\x1c\x4e\x56\x32\xa3\x16\x06\x70\x01\xdc\x00\xe0\x00\x1e\x30\x97\x92\x35\x01\xa0\xf2\x7a\xf9\xe6\x22\x0b\x30\x80\xfc\x80\x1a\xcc\xbf\x41\x16\xa7\x17\xfe\xae\xd6\xf7\xd4\x32\x89\x8b\x35\x97\x74\x66\xe9\xda\x2e\xf3\x01\xc4\x34\x20\x85\x0b\x3b\x80\x08\x99\x98\x1a\x58\x00\xf8\x12\xba\x65\xae\xf4\x45\x72\x0b\xc0\x01\x20\xd5\x53\x05\xf1\xb8\xf0\xd7\x06\x62\x61\x3f\x2a\x06\x76\x40\xfe\x60\x47\x00\x8a\x4c\xa7\x00\x40\x81\x6a\xa6\x17\xf8\x70\x47\x68\x15\x74\x2b\xac\x02\x00\x0e\x00\x78\x01\x98\x20\x77\x6d\x53\x02\x3a\x01\x58\x38\x03\xe6\xc2\x7a\xae\x32\xc4\x36\xc3\x23\x13\xfa\xcd\x3a\x05\x23\x5d\x63\xf2\x64\x0a\x6a\x71\xbf\xa8\xac\xf2\x61\x3f\xe4\x0c\x3e\xf1\x5a\x51\x01\x86\x00\x3c\x02\xa8\x56\x2a\x20\x04\x28\x0c\x3f\x26\x8c\xc2\x40\x88\x00\x3a\x71\x3f\x6a\x4d\xef\x42\x41\x08\x7c\x19\xc0\xcf\x4c\xed\xc4\xb4\x51\x6c\xfd\x93\xea\x39\xe8\xc7\x5f\xd6\x02\xe8\x54\x2a\x3d\x80\x0c\x95\x30\x80\x54\xb5\x01\xbc\xf9\xed\x74\xc4\x7e\x2a\x6f\x8e\xae\xc6\xd3\x6f\xf5\x6a\xf4\x6f\x67\xf7\x22\xe2\x38\x80\x98\x33\xe5\x0d\x38\xca\x7d\x07\x7b\xa5\xdb\x2e\x53\xeb\x44\xe5\x2b\x03\xee\x0c\xdc\xe6\x5c\xee\x36\x85\x81\xbf\xc4\x0c\x00\x15\x7f\x5b\x03\x8c\x99\xe4\xb0\x1f\x9d\xc6\x19\x70\x47\xdc\xd1\x7d\xf0\x6d\x61\x77\xc8\xda\x80\x70\xe2\x0a\x10\x89\xbb\x16\xf1\x84\xfd\x12\x03\xaa\x90\x09\x60\x10\x3f\x00\xf4\x21\x1b\x6b\x01\x98\x8c\x15\x24\x02\xfc\xc6\x57\xe4\x5d\xb0\x39\x20\x98\x53\xf9\x98\xf4\x02\x40\x6a\x80\x1c\xc0\x51\x66\xc0\x55\xe8\x6c\xf9\x6d\xbc\x84\xbc\x2c\x54\x5f\xfc\xd0\x5a\xce\x30\x5d\x02\x10\x93\x43\xdc\xf1\x98\x28\x03\xa0\x66\xa0\x28\xdd\x0f\xd0\x1c\xa0\x31\x4c\x9a\xde\xba\x6b\x65\xf7\xcb\x4c\xbf\x85\x58\xe8\xec\x0e\xf9\x51\x37\xe3\x09\x00\x5b\x7b\x0b\x3c\xdf\xd2\xed\xeb\x66\x7b\x6b\xd9\x17\x37\xb5\xbd\x9c\x77\xf7\xb6\xfd\xff\xb9\xfc\x60\x8b\xdb\x7b\xca\x13\x3a\xdd\x63\xb6\x78\xb0\xe1\xfd\x2f\x00\x00\xff\xff\xfb\x08\x01\x0a\x00\x20\x00\x00")
func dbMigrationsAssetsGoBytes() ([]byte, error) { func dbMigrationsAssetsGoBytes() ([]byte, error) {
return bindataRead( return bindataRead(
...@@ -209,7 +230,7 @@ func dbMigrationsAssetsGo() (*asset, error) { ...@@ -209,7 +230,7 @@ func dbMigrationsAssetsGo() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "db/migrations/assets.go", size: 16384, mode: os.FileMode(420), modTime: time.Unix(1, 0)} info := bindataFileInfo{name: "db/migrations/assets.go", size: 20480, mode: os.FileMode(436), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
...@@ -272,6 +293,7 @@ var _bindata = map[string]func() (*asset, error){ ...@@ -272,6 +293,7 @@ var _bindata = map[string]func() (*asset, error){
"db/migrations/0004_session_nonce.sql": dbMigrations0004_session_nonceSql, "db/migrations/0004_session_nonce.sql": dbMigrations0004_session_nonceSql,
"db/migrations/0005_refresh_token_create.sql": dbMigrations0005_refresh_token_createSql, "db/migrations/0005_refresh_token_create.sql": dbMigrations0005_refresh_token_createSql,
"db/migrations/0006_user_email_unique.sql": dbMigrations0006_user_email_uniqueSql, "db/migrations/0006_user_email_unique.sql": dbMigrations0006_user_email_uniqueSql,
"db/migrations/0007_session_scope.sql": dbMigrations0007_session_scopeSql,
"db/migrations/assets.go": dbMigrationsAssetsGo, "db/migrations/assets.go": dbMigrationsAssetsGo,
} }
...@@ -324,6 +346,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ ...@@ -324,6 +346,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"0004_session_nonce.sql": &bintree{dbMigrations0004_session_nonceSql, map[string]*bintree{}}, "0004_session_nonce.sql": &bintree{dbMigrations0004_session_nonceSql, map[string]*bintree{}},
"0005_refresh_token_create.sql": &bintree{dbMigrations0005_refresh_token_createSql, map[string]*bintree{}}, "0005_refresh_token_create.sql": &bintree{dbMigrations0005_refresh_token_createSql, map[string]*bintree{}},
"0006_user_email_unique.sql": &bintree{dbMigrations0006_user_email_uniqueSql, map[string]*bintree{}}, "0006_user_email_unique.sql": &bintree{dbMigrations0006_user_email_uniqueSql, map[string]*bintree{}},
"0007_session_scope.sql": &bintree{dbMigrations0007_session_scopeSql, map[string]*bintree{}},
"assets.go": &bintree{dbMigrationsAssetsGo, map[string]*bintree{}}, "assets.go": &bintree{dbMigrationsAssetsGo, map[string]*bintree{}},
}}, }},
}}, }},
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/go-gorp/gorp" "github.com/go-gorp/gorp"
...@@ -42,6 +43,7 @@ type sessionModel struct { ...@@ -42,6 +43,7 @@ type sessionModel struct {
UserID string `db:"user_id"` UserID string `db:"user_id"`
Register bool `db:"register"` Register bool `db:"register"`
Nonce string `db:"nonce"` Nonce string `db:"nonce"`
Scope string `db:"scope"`
} }
func (s *sessionModel) session() (*session.Session, error) { func (s *sessionModel) session() (*session.Session, error) {
...@@ -71,6 +73,7 @@ func (s *sessionModel) session() (*session.Session, error) { ...@@ -71,6 +73,7 @@ func (s *sessionModel) session() (*session.Session, error) {
UserID: s.UserID, UserID: s.UserID,
Register: s.Register, Register: s.Register,
Nonce: s.Nonce, Nonce: s.Nonce,
Scope: strings.Fields(s.Scope),
} }
if s.CreatedAt != 0 { if s.CreatedAt != 0 {
...@@ -101,6 +104,7 @@ func newSessionModel(s *session.Session) (*sessionModel, error) { ...@@ -101,6 +104,7 @@ func newSessionModel(s *session.Session) (*sessionModel, error) {
UserID: s.UserID, UserID: s.UserID,
Register: s.Register, Register: s.Register,
Nonce: s.Nonce, Nonce: s.Nonce,
Scope: strings.Join(s.Scope, " "),
} }
if !s.CreatedAt.IsZero() { if !s.CreatedAt.IsZero() {
......
...@@ -196,7 +196,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) { ...@@ -196,7 +196,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
// this will actually happen due to some interaction between the // this will actually happen due to some interaction between the
// end-user and a remote identity provider // end-user and a remote identity provider
sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
......
...@@ -330,9 +330,37 @@ func handleAuthFunc(srv OIDCServer, idpcs []connector.Connector, tpl *template.T ...@@ -330,9 +330,37 @@ func handleAuthFunc(srv OIDCServer, idpcs []connector.Connector, tpl *template.T
return return
} }
// Check scopes.
var scopes []string
foundOpenIDScope := false
for _, scope := range acr.Scope {
switch scope {
case "openid":
foundOpenIDScope = true
scopes = append(scopes, scope)
case "offline_access":
// According to the spec, for offline_access scope, the client must
// use a response_type value that would result in an Authorization Code.
// Currently oauth2.ResponseTypeCode is the only supported response type,
// and it's been checked above, so we don't need to check it again here.
//
// TODO(yifan): Verify that 'consent' should be in 'prompt'.
scopes = append(scopes, scope)
default:
// Pass all other scopes.
scopes = append(scopes, scope)
}
}
if !foundOpenIDScope {
log.Errorf("Invalid auth request: missing 'openid' in 'scope'")
writeAuthError(w, oauth2.NewError(oauth2.ErrorInvalidRequest), acr.State)
return
}
nonce := q.Get("nonce") nonce := q.Get("nonce")
key, err := srv.NewSession(connectorID, acr.ClientID, acr.State, redirectURL, nonce, register) key, err := srv.NewSession(connectorID, acr.ClientID, acr.State, redirectURL, nonce, register, acr.Scope)
if err != nil { if err != nil {
log.Errorf("Error creating new session: %v: ", err) log.Errorf("Error creating new session: %v: ", err)
redirectAuthError(w, err, acr.State, redirectURL) redirectAuthError(w, err, acr.State, redirectURL)
......
...@@ -102,6 +102,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { ...@@ -102,6 +102,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
"response_type": []string{"code"}, "response_type": []string{"code"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusTemporaryRedirect, wantCode: http.StatusTemporaryRedirect,
wantLocation: "http://fake.example.com", wantLocation: "http://fake.example.com",
...@@ -114,6 +115,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { ...@@ -114,6 +115,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
"redirect_uri": []string{"http://client.example.com/callback"}, "redirect_uri": []string{"http://client.example.com/callback"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusTemporaryRedirect, wantCode: http.StatusTemporaryRedirect,
wantLocation: "http://fake.example.com", wantLocation: "http://fake.example.com",
...@@ -126,6 +128,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { ...@@ -126,6 +128,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
"redirect_uri": []string{"http://unrecognized.example.com/callback"}, "redirect_uri": []string{"http://unrecognized.example.com/callback"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusBadRequest, wantCode: http.StatusBadRequest,
}, },
...@@ -137,6 +140,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { ...@@ -137,6 +140,7 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
"redirect_uri": []string{"http://client.example.com/callback"}, "redirect_uri": []string{"http://client.example.com/callback"},
"client_id": []string{"YYY"}, "client_id": []string{"YYY"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusBadRequest, wantCode: http.StatusBadRequest,
}, },
...@@ -147,10 +151,22 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { ...@@ -147,10 +151,22 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
"response_type": []string{"token"}, "response_type": []string{"token"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusTemporaryRedirect, wantCode: http.StatusTemporaryRedirect,
wantLocation: "http://client.example.com/callback?error=unsupported_response_type&state=", wantLocation: "http://client.example.com/callback?error=unsupported_response_type&state=",
}, },
// no 'openid' in scope
{
query: url.Values{
"response_type": []string{"code"},
"redirect_uri": []string{"http://client.example.com/callback"},
"client_id": []string{"XXX"},
"connector_id": []string{"fake"},
},
wantCode: http.StatusBadRequest,
},
} }
for i, tt := range tests { for i, tt := range tests {
...@@ -211,6 +227,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) { ...@@ -211,6 +227,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) {
"redirect_uri": []string{"http://foo.example.com/callback"}, "redirect_uri": []string{"http://foo.example.com/callback"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusTemporaryRedirect, wantCode: http.StatusTemporaryRedirect,
wantLocation: "http://fake.example.com", wantLocation: "http://fake.example.com",
...@@ -223,6 +240,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) { ...@@ -223,6 +240,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) {
"redirect_uri": []string{"http://bar.example.com/callback"}, "redirect_uri": []string{"http://bar.example.com/callback"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusTemporaryRedirect, wantCode: http.StatusTemporaryRedirect,
wantLocation: "http://fake.example.com", wantLocation: "http://fake.example.com",
...@@ -235,6 +253,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) { ...@@ -235,6 +253,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) {
"redirect_uri": []string{"http://unrecognized.example.com/callback"}, "redirect_uri": []string{"http://unrecognized.example.com/callback"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusBadRequest, wantCode: http.StatusBadRequest,
}, },
...@@ -245,6 +264,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) { ...@@ -245,6 +264,7 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) {
"response_type": []string{"code"}, "response_type": []string{"code"},
"client_id": []string{"XXX"}, "client_id": []string{"XXX"},
"connector_id": []string{"fake"}, "connector_id": []string{"fake"},
"scope": []string{"openid"},
}, },
wantCode: http.StatusBadRequest, wantCode: http.StatusBadRequest,
}, },
......
...@@ -245,7 +245,7 @@ func TestSendResetPasswordEmailHandler(t *testing.T) { ...@@ -245,7 +245,7 @@ func TestSendResetPasswordEmailHandler(t *testing.T) {
t.Fatalf("case %d: could not make test fixtures: %v", i, err) t.Fatalf("case %d: could not make test fixtures: %v", i, err)
} }
_, err = f.srv.NewSession("local", "XXX", "", f.redirectURL, "", true) _, err = f.srv.NewSession("local", "XXX", "", f.redirectURL, "", true, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("case %d: could not create new session: %v", i, err) t.Fatalf("case %d: could not create new session: %v", i, err)
} }
......
...@@ -197,7 +197,7 @@ func TestHandleRegister(t *testing.T) { ...@@ -197,7 +197,7 @@ func TestHandleRegister(t *testing.T) {
t.Fatalf("case %d: could not make test fixtures: %v", i, err) t.Fatalf("case %d: could not make test fixtures: %v", i, err)
} }
key, err := f.srv.NewSession(tt.connID, "XXX", "", f.redirectURL, "", true) key, err := f.srv.NewSession(tt.connID, "XXX", "", f.redirectURL, "", true, []string{"openid"})
t.Logf("case %d: key for NewSession: %v", i, key) t.Logf("case %d: key for NewSession: %v", i, key)
if tt.attachRemote { if tt.attachRemote {
......
...@@ -39,7 +39,7 @@ const ( ...@@ -39,7 +39,7 @@ const (
type OIDCServer interface { type OIDCServer interface {
ClientMetadata(string) (*oidc.ClientMetadata, error) ClientMetadata(string) (*oidc.ClientMetadata, error)
NewSession(connectorID, clientID, clientState string, redirectURL url.URL, nonce string, register bool) (string, error) NewSession(connectorID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error)
Login(oidc.Identity, string) (string, error) Login(oidc.Identity, string) (string, error)
// CodeToken exchanges a code for an ID token and a refresh token string on success. // CodeToken exchanges a code for an ID token and a refresh token string on success.
CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jose.JWT, string, error) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jose.JWT, string, error)
...@@ -263,8 +263,8 @@ func (s *Server) ClientMetadata(clientID string) (*oidc.ClientMetadata, error) { ...@@ -263,8 +263,8 @@ func (s *Server) ClientMetadata(clientID string) (*oidc.ClientMetadata, error) {
return s.ClientIdentityRepo.Metadata(clientID) return s.ClientIdentityRepo.Metadata(clientID)
} }
func (s *Server) NewSession(ipdcID, clientID, clientState string, redirectURL url.URL, nonce string, register bool) (string, error) { func (s *Server) NewSession(ipdcID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error) {
sessionID, err := s.SessionManager.NewSession(ipdcID, clientID, clientState, redirectURL, nonce, register) sessionID, err := s.SessionManager.NewSession(ipdcID, clientID, clientState, redirectURL, nonce, register, scope)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -422,13 +422,14 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo ...@@ -422,13 +422,14 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo
return nil, "", oauth2.NewError(oauth2.ErrorServerError) return nil, "", oauth2.NewError(oauth2.ErrorServerError)
} }
log.Infof("Session %s token sent: clientID=%s", sessionID, creds.ID) // Generate refresh token when 'scope' contains 'offline_access'.
var refreshToken string
for _, scope := range ses.Scope {
if scope == "offline_access" {
log.Infof("Session %s requests offline access, will generate refresh token", sessionID)
// Generate refresh token. refreshToken, err = s.RefreshTokenRepo.Create(ses.UserID, creds.ID)
//
// TODO(yifan): Return refresh token only when 'access_type == offline',
// or 'scope' == 'offline_access'.
refreshToken, err := s.RefreshTokenRepo.Create(ses.UserID, creds.ID)
switch err { switch err {
case nil: case nil:
break break
...@@ -436,6 +437,11 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo ...@@ -436,6 +437,11 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo
log.Errorf("Failed to generate refresh token: %v", err) log.Errorf("Failed to generate refresh token: %v", err)
return nil, "", oauth2.NewError(oauth2.ErrorServerError) return nil, "", oauth2.NewError(oauth2.ErrorServerError)
} }
break
}
}
log.Infof("Session %s token sent: clientID=%s", sessionID, creds.ID)
return jwt, refreshToken, nil return jwt, refreshToken, nil
} }
...@@ -487,7 +493,7 @@ func (s *Server) RefreshToken(creds oidc.ClientCredentials, token string) (*jose ...@@ -487,7 +493,7 @@ func (s *Server) RefreshToken(creds oidc.ClientCredentials, token string) (*jose
return nil, oauth2.NewError(oauth2.ErrorServerError) return nil, oauth2.NewError(oauth2.ErrorServerError)
} }
log.Infof("Token refreshed sent: clientID=%s", creds.ID) log.Infof("New token sent: clientID=%s", creds.ID)
return jwt, nil return jwt, nil
} }
......
...@@ -2,6 +2,7 @@ package server ...@@ -2,6 +2,7 @@ package server
import ( import (
"errors" "errors"
"fmt"
"net/url" "net/url"
"reflect" "reflect"
"testing" "testing"
...@@ -139,7 +140,7 @@ func TestServerNewSession(t *testing.T) { ...@@ -139,7 +140,7 @@ func TestServerNewSession(t *testing.T) {
}, },
} }
key, err := srv.NewSession("bogus_idpc", ci.Credentials.ID, state, ci.Metadata.RedirectURLs[0], nonce, false) key, err := srv.NewSession("bogus_idpc", ci.Credentials.ID, state, ci.Metadata.RedirectURLs[0], nonce, false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -195,7 +196,7 @@ func TestServerLogin(t *testing.T) { ...@@ -195,7 +196,7 @@ func TestServerLogin(t *testing.T) {
sm := session.NewSessionManager(session.NewSessionRepo(), session.NewSessionKeyRepo()) sm := session.NewSessionManager(session.NewSessionRepo(), session.NewSessionKeyRepo())
sm.GenerateCode = staticGenerateCodeFunc("fakecode") sm.GenerateCode = staticGenerateCodeFunc("fakecode")
sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURLs[0], "", false) sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURLs[0], "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -292,34 +293,52 @@ func TestServerCodeToken(t *testing.T) { ...@@ -292,34 +293,52 @@ func TestServerCodeToken(t *testing.T) {
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false) tests := []struct {
scope []string
refreshToken string
}{
// No 'offline_access' in scope, should get empty refresh token.
{
scope: []string{"openid"},
refreshToken: "",
},
// Have 'offline_access' in scope, should get non-empty refresh token.
{
scope: []string{"openid", "offline_access"},
refreshToken: "0/refresh-1",
},
}
for i, tt := range tests {
sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, tt.scope)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{}) _, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
_, err = sm.AttachUser(sessionID, "testid-1") _, err = sm.AttachUser(sessionID, "testid-1")
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
key, err := sm.NewSessionKey(sessionID) key, err := sm.NewSessionKey(sessionID)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
jwt, token, err := srv.CodeToken(ci.Credentials, key) jwt, token, err := srv.CodeToken(ci.Credentials, key)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
if jwt == nil { if jwt == nil {
t.Fatalf("Expected non-nil jwt") t.Fatalf("case %d: expect non-nil jwt", i)
}
if token != tt.refreshToken {
t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
} }
if token == "" {
t.Fatalf("Expected non-empty refresh token")
} }
} }
...@@ -343,7 +362,7 @@ func TestServerTokenUnrecognizedKey(t *testing.T) { ...@@ -343,7 +362,7 @@ func TestServerTokenUnrecognizedKey(t *testing.T) {
ClientIdentityRepo: ciRepo, ClientIdentityRepo: ciRepo,
} }
sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -379,12 +398,24 @@ func TestServerTokenFail(t *testing.T) { ...@@ -379,12 +398,24 @@ func TestServerTokenFail(t *testing.T) {
argCC oidc.ClientCredentials argCC oidc.ClientCredentials
argKey string argKey string
err string err string
scope []string
refreshToken string
}{ }{
// control test case to make sure fixtures check out // control test case to make sure fixtures check out
{ {
signer: signerFixture, signer: signerFixture,
argCC: ccFixture, argCC: ccFixture,
argKey: keyFixture, argKey: keyFixture,
scope: []string{"openid", "offline_access"},
refreshToken: "0/refresh-1",
},
// no 'offline_access' in 'scope', should get empty refresh token
{
signer: signerFixture,
argCC: ccFixture,
argKey: keyFixture,
scope: []string{"openid"},
}, },
// unrecognized key // unrecognized key
...@@ -393,6 +424,7 @@ func TestServerTokenFail(t *testing.T) { ...@@ -393,6 +424,7 @@ func TestServerTokenFail(t *testing.T) {
argCC: ccFixture, argCC: ccFixture,
argKey: "foo", argKey: "foo",
err: oauth2.ErrorInvalidGrant, err: oauth2.ErrorInvalidGrant,
scope: []string{"openid", "offline_access"},
}, },
// unrecognized client // unrecognized client
...@@ -401,6 +433,7 @@ func TestServerTokenFail(t *testing.T) { ...@@ -401,6 +433,7 @@ func TestServerTokenFail(t *testing.T) {
argCC: oidc.ClientCredentials{ID: "YYY"}, argCC: oidc.ClientCredentials{ID: "YYY"},
argKey: keyFixture, argKey: keyFixture,
err: oauth2.ErrorInvalidClient, err: oauth2.ErrorInvalidClient,
scope: []string{"openid", "offline_access"},
}, },
// signing operation fails // signing operation fails
...@@ -409,6 +442,7 @@ func TestServerTokenFail(t *testing.T) { ...@@ -409,6 +442,7 @@ func TestServerTokenFail(t *testing.T) {
argCC: ccFixture, argCC: ccFixture,
argKey: keyFixture, argKey: keyFixture,
err: oauth2.ErrorServerError, err: oauth2.ErrorServerError,
scope: []string{"openid", "offline_access"},
}, },
} }
...@@ -416,7 +450,7 @@ func TestServerTokenFail(t *testing.T) { ...@@ -416,7 +450,7 @@ func TestServerTokenFail(t *testing.T) {
sm := session.NewSessionManager(session.NewSessionRepo(), session.NewSessionKeyRepo()) sm := session.NewSessionManager(session.NewSessionRepo(), session.NewSessionKeyRepo())
sm.GenerateCode = func() (string, error) { return keyFixture, nil } sm.GenerateCode = func() (string, error) { return keyFixture, nil }
sessionID, err := sm.NewSession("connector_id", ccFixture.ID, "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("connector_id", ccFixture.ID, "bogus", url.URL{}, "", false, tt.scope)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -435,7 +469,7 @@ func TestServerTokenFail(t *testing.T) { ...@@ -435,7 +469,7 @@ func TestServerTokenFail(t *testing.T) {
_, err = sm.AttachUser(sessionID, "testid-1") _, err = sm.AttachUser(sessionID, "testid-1")
if err != nil { if err != nil {
t.Fatalf("case %d: Unexpected error: %v", i, err) t.Fatalf("case %d: unexpected error: %v", i, err)
} }
userRepo, err := makeNewUserRepo() userRepo, err := makeNewUserRepo()
...@@ -463,22 +497,22 @@ func TestServerTokenFail(t *testing.T) { ...@@ -463,22 +497,22 @@ func TestServerTokenFail(t *testing.T) {
} }
jwt, token, err := srv.CodeToken(tt.argCC, tt.argKey) jwt, token, err := srv.CodeToken(tt.argCC, tt.argKey)
if token != tt.refreshToken {
fmt.Printf("case %d: expect refresh token %q, got %q\n", i, tt.refreshToken, token)
t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
panic("")
}
if tt.err == "" { if tt.err == "" {
if err != nil { if err != nil {
t.Errorf("case %d: got non-nil error: %v", i, err) t.Errorf("case %d: got non-nil error: %v", i, err)
} else if jwt == nil { } else if jwt == nil {
t.Errorf("case %d: got nil JWT", i) t.Errorf("case %d: got nil JWT", i)
} else if token == "" {
t.Errorf("case %d: got empty refresh token", i)
} }
} else { } else {
if err.Error() != tt.err { if err.Error() != tt.err {
t.Errorf("case %d: want err %q, got %q", i, tt.err, err.Error()) t.Errorf("case %d: want err %q, got %q", i, tt.err, err.Error())
} else if jwt != nil { } else if jwt != nil {
t.Errorf("case %d: got non-nil JWT", i) t.Errorf("case %d: got non-nil JWT", i)
} else if token != "" {
t.Errorf("case %d: got non-empty refresh token", i)
} }
} }
} }
......
...@@ -44,7 +44,7 @@ type SessionManager struct { ...@@ -44,7 +44,7 @@ type SessionManager struct {
keys SessionKeyRepo keys SessionKeyRepo
} }
func (m *SessionManager) NewSession(connectorID, clientID, clientState string, redirectURL url.URL, nonce string, register bool) (string, error) { func (m *SessionManager) NewSession(connectorID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error) {
sID, err := m.GenerateCode() sID, err := m.GenerateCode()
if err != nil { if err != nil {
return "", err return "", err
...@@ -62,6 +62,7 @@ func (m *SessionManager) NewSession(connectorID, clientID, clientState string, r ...@@ -62,6 +62,7 @@ func (m *SessionManager) NewSession(connectorID, clientID, clientState string, r
RedirectURL: redirectURL, RedirectURL: redirectURL,
Register: register, Register: register,
Nonce: nonce, Nonce: nonce,
Scope: scope,
} }
err = m.sessions.Create(s) err = m.sessions.Create(s)
......
...@@ -16,7 +16,7 @@ func staticGenerateCodeFunc(code string) GenerateCodeFunc { ...@@ -16,7 +16,7 @@ func staticGenerateCodeFunc(code string) GenerateCodeFunc {
func TestSessionManagerNewSession(t *testing.T) { func TestSessionManagerNewSession(t *testing.T) {
sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo()) sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo())
sm.GenerateCode = staticGenerateCodeFunc("boo") sm.GenerateCode = staticGenerateCodeFunc("boo")
got, err := sm.NewSession("bogus_idpc", "XXX", "bogus", url.URL{}, "", false) got, err := sm.NewSession("bogus_idpc", "XXX", "bogus", url.URL{}, "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -27,7 +27,7 @@ func TestSessionManagerNewSession(t *testing.T) { ...@@ -27,7 +27,7 @@ func TestSessionManagerNewSession(t *testing.T) {
func TestSessionAttachRemoteIdentityTwice(t *testing.T) { func TestSessionAttachRemoteIdentityTwice(t *testing.T) {
sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo()) sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo())
sessionID, err := sm.NewSession("bogus_idpc", "XXX", "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("bogus_idpc", "XXX", "bogus", url.URL{}, "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -44,7 +44,7 @@ func TestSessionAttachRemoteIdentityTwice(t *testing.T) { ...@@ -44,7 +44,7 @@ func TestSessionAttachRemoteIdentityTwice(t *testing.T) {
func TestSessionManagerExchangeKey(t *testing.T) { func TestSessionManagerExchangeKey(t *testing.T) {
sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo()) sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo())
sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -80,7 +80,7 @@ func TestSessionManagerGetSessionInStateNoExist(t *testing.T) { ...@@ -80,7 +80,7 @@ func TestSessionManagerGetSessionInStateNoExist(t *testing.T) {
func TestSessionManagerGetSessionInStateWrongState(t *testing.T) { func TestSessionManagerGetSessionInStateWrongState(t *testing.T) {
sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo()) sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo())
sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
...@@ -95,7 +95,7 @@ func TestSessionManagerGetSessionInStateWrongState(t *testing.T) { ...@@ -95,7 +95,7 @@ func TestSessionManagerGetSessionInStateWrongState(t *testing.T) {
func TestSessionManagerKill(t *testing.T) { func TestSessionManagerKill(t *testing.T) {
sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo()) sm := NewSessionManager(NewSessionRepo(), NewSessionKeyRepo())
sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false) sessionID, err := sm.NewSession("connector_id", "XXX", "bogus", url.URL{}, "", false, []string{"openid"})
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
......
...@@ -48,6 +48,9 @@ type Session struct { ...@@ -48,6 +48,9 @@ type Session struct {
// Nonce is optionally provided in the initial authorization request, and propogated in such cases to the generated claims. // Nonce is optionally provided in the initial authorization request, and propogated in such cases to the generated claims.
Nonce string Nonce string
// Scope is the 'scope' field in the authentication request. Example scopes are 'openid', 'email', 'offline', etc.
Scope []string
} }
// Claims returns a new set of Claims for the current session. // Claims returns a new set of Claims for the current session.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment