Repository: dragonflyoss/Dragonfly2
Pull request #591 (2021-08-31 10:17:25)

feat: oauth2
Signed-off-by: Gaius <gaius.qi@gmail.com>

<!--- Provide a general summary of your changes in the Title above -->

## Description
- Add oauth.
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate):

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] Code compiles correctly.
- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#591](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (4a36431) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/86c90ea69d77771ee9e8b6ded613bcd407d1a9d4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (86c90ea) will **decrease** coverage by `0.13%`.
> The diff coverage is `10.00%`.

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main     #591      +/-   ##
==========================================
- Coverage   39.11%   38.98%   -0.14%     
==========================================
  Files          86       86              
  Lines        6701     6716      +15     
==========================================
- Hits         2621     2618       -3     
- Misses       3708     3726      +18     
  Partials      372      372              
```

| Flag | Coverage Δ | |
|---|---|---|
| e2etests | `?` | |
| unittests | `38.98% <10.00%> (-0.14%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [manager/permission/rbac/rbac.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-bWFuYWdlci9wZXJtaXNzaW9uL3JiYWMvcmJhYy5nbw==) | `16.66% <10.00%> (-6.42%)` | :arrow_down: |
| [client/daemon/peer/peertask\_base.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/591/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX2Jhc2UuZ28=) | `32.86% <0.00%> (-0.61%)` | :arrow_down: |


Patch:
diff --git a/api/manager/docs.go b/api/manager/docs.go
index 2eb10f60..927a4ec1 100644
--- a/api/manager/docs.go
+++ b/api/manager/docs.go
@@ -590,9 +590,472 @@ var doc = `{
                 }
             }
         },
+        "/oauth": {
+            "get": {
+                "description": "Get Oauths",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Get Oauths",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "default": 0,
+                        "description": "current page",
+                        "name": "page",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "maximum": 50,
+                        "minimum": 2,
+                        "type": "integer",
+                        "default": 10,
+                        "description": "return max item count, default 10, max 50",
+                        "name": "per_page",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/model.Oauth"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "create by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Create Oauth",
+                "parameters": [
+                    {
+                        "description": "Oauth",
+                        "name": "Oauth",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateOauthRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/oauth/{id}": {
+            "get": {
+                "description": "Get Oauth by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Get Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Destroy Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Update Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Oauth",
+                        "name": "Oauth",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateOauthRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/permissions": {
+            "get": {
+                "description": "Get Permissions",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Permission"
+                ],
+                "summary": "Get Permissions",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/rbac.Permission"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
         "/preheats": {
             "post": {
-                "description": "create by json config",
+                "description": "create by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Preheat"
+                ],
+                "summary": "Create Preheat",
+                "parameters": [
+                    {
+                        "description": "Preheat",
+                        "name": "CDN",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreatePreheatRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Preheat"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/preheats/{id}": {
+            "get": {
+                "description": "Get Preheat by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Preheat"
+                ],
+                "summary": "Get Preheat",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Preheat"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles": {
+            "get": {
+                "description": "Get roles",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Get Roles",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create Role by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Create Role",
+                "parameters": [
+                    {
+                        "description": "Role",
+                        "name": "Role",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateRoleRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles/:role": {
+            "get": {
+                "description": "Get Role",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Get Role",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy role by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Destroy Role",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles/:role/permissions": {
+            "post": {
+                "description": "Add Permission by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -600,42 +1063,41 @@ var doc = `{
                     "application/json"
                 ],
                 "tags": [
-                    "Preheat"
+                    "Role"
                 ],
-                "summary": "Create Preheat",
+                "summary": "Add Permission For Role",
                 "parameters": [
                     {
-                        "description": "Preheat",
-                        "name": "CDN",
+                        "description": "Permission",
+                        "name": "Permission",
                         "in": "body",
                         "required": true,
                         "schema": {
-                            "$ref": "#/definitions/types.CreatePreheatRequest"
+                            "$ref": "#/definitions/types.AddPermissionForRoleRequest"
                         }
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/types.Preheat"
-                        }
-                    },
-                    "400": {
                         "description": ""
                     },
-                    "404": {
+                    "400": {
                         "description": ""
                     },
                     "500": {
                         "description": ""
                     }
                 }
-            }
-        },
-        "/preheats/{id}": {
-            "get": {
-                "description": "Get Preheat by id",
+            },
+            "delete": {
+                "description": "Remove Role Permission by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -643,29 +1105,32 @@ var doc = `{
                     "application/json"
                 ],
                 "tags": [
-                    "Preheat"
+                    "Role"
                 ],
-                "summary": "Get Preheat",
+                "summary": "Update Role",
                 "parameters": [
+                    {
+                        "description": "Permission",
+                        "name": "Permission",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.DeletePermissionForRoleRequest"
+                        }
+                    },
                     {
                         "type": "string",
-                        "description": "id",
-                        "name": "id",
+                        "description": "role",
+                        "name": "role",
                         "in": "path",
                         "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/types.Preheat"
-                        }
-                    },
-                    "400": {
                         "description": ""
                     },
-                    "404": {
+                    "400": {
                         "description": ""
                     },
                     "500": {
@@ -1382,28 +1847,262 @@ var doc = `{
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/security-groups/{id}/scheduler-clusters/{scheduler_cluster_id}": {
+            "put": {
+                "description": "Add Scheduler to SecurityGroup",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SecurityGroup"
+                ],
+                "summary": "Add Scheduler to SecurityGroup",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "scheduler cluster id",
+                        "name": "scheduler_cluster_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/securityGroups/{id}": {
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SecurityGroup"
+                ],
+                "summary": "Destroy SecurityGroup",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signin/{name}": {
+            "get": {
+                "description": "oauth signin by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Oauth Signin",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "name",
+                        "name": "name",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signin/{name}/callback": {
+            "get": {
+                "description": "oauth signin callback by json config",
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Oauth Signin Callback",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "name",
+                        "name": "name",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "code",
+                        "name": "code",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signup": {
+            "post": {
+                "description": "signup by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "SignUp user",
+                "parameters": [
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.SignUpRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.User"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/users/:id/reset_password": {
+            "post": {
+                "description": "reset password by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Reset Password For User",
+                "parameters": [
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.ResetPasswordRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
                     "500": {
                         "description": ""
                     }
                 }
             }
         },
-        "/security-groups/{id}/scheduler-clusters/{scheduler_cluster_id}": {
-            "put": {
-                "description": "Add Scheduler to SecurityGroup",
-                "consumes": [
-                    "application/json"
-                ],
+        "/users/:id/roles": {
+            "get": {
+                "description": "get roles by json config",
                 "produces": [
                     "application/json"
                 ],
                 "tags": [
-                    "SecurityGroup"
+                    "User"
                 ],
-                "summary": "Add Scheduler to SecurityGroup",
+                "summary": "Get User Roles",
                 "parameters": [
                     {
                         "type": "string",
@@ -1411,34 +2110,30 @@ var doc = `{
                         "name": "id",
                         "in": "path",
                         "required": true
-                    },
-                    {
-                        "type": "string",
-                        "description": "scheduler cluster id",
-                        "name": "scheduler_cluster_id",
-                        "in": "path",
-                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": ""
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            }
+                        }
                     },
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
                     "500": {
                         "description": ""
                     }
                 }
             }
         },
-        "/securityGroups/{id}": {
-            "delete": {
-                "description": "Destroy by id",
+        "/users/:id/roles/:role": {
+            "put": {
+                "description": "add role to user by uri config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1446,9 +2141,9 @@ var doc = `{
                     "application/json"
                 ],
                 "tags": [
-                    "SecurityGroup"
+                    "Users"
                 ],
-                "summary": "Destroy SecurityGroup",
+                "summary": "Add Role For User",
                 "parameters": [
                     {
                         "type": "string",
@@ -1456,6 +2151,13 @@ var doc = `{
                         "name": "id",
                         "in": "path",
                         "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
@@ -1465,18 +2167,13 @@ var doc = `{
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
                     "500": {
                         "description": ""
                     }
                 }
-            }
-        },
-        "/user/signup": {
-            "post": {
-                "description": "SignUp user by json config",
+            },
+            "delete": {
+                "description": "delete role by uri config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1484,26 +2181,28 @@ var doc = `{
                     "application/json"
                 ],
                 "tags": [
-                    "User"
+                    "Users"
                 ],
-                "summary": "SignUp user",
+                "summary": "Delete Role For User",
                 "parameters": [
                     {
-                        "description": "User",
-                        "name": "User",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "$ref": "#/definitions/types.SignUpRequest"
-                        }
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/model.User"
-                        }
+                        "description": ""
                     },
                     "400": {
                         "description": ""
@@ -1516,33 +2215,65 @@ var doc = `{
         }
     },
     "definitions": {
+        "model.Assertion": {
+            "type": "object",
+            "properties": {
+                "key": {
+                    "type": "string"
+                },
+                "policy": {
+                    "type": "array",
+                    "items": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    }
+                },
+                "policyMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "rm": {
+                    "$ref": "#/definitions/rbac.RoleManager"
+                },
+                "tokens": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "value": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.AssertionMap": {
+            "type": "object",
+            "additionalProperties": {
+                "$ref": "#/definitions/model.Assertion"
+            }
+        },
         "model.CDN": {
             "type": "object",
             "properties": {
                 "cdnclusterID": {
                     "type": "integer"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "download_port": {
                     "type": "integer"
                 },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
                 "ip": {
                     "type": "string"
                 },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1551,9 +2282,6 @@ var doc = `{
                 },
                 "status": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1566,15 +2294,6 @@ var doc = `{
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -1583,9 +2302,6 @@ var doc = `{
                 },
                 "securityGroupID": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1593,17 +2309,31 @@ var doc = `{
             "type": "object",
             "additionalProperties": true
         },
-        "model.Scheduler": {
+        "model.Oauth": {
             "type": "object",
             "properties": {
-                "created_at": {
+                "bio": {
                     "type": "string"
                 },
-                "host_name": {
+                "client_id": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.Scheduler": {
+            "type": "object",
+            "properties": {
+                "host_name": {
+                    "type": "string"
                 },
                 "idc": {
                     "type": "string"
@@ -1611,9 +2341,6 @@ var doc = `{
                 "ip": {
                     "type": "string"
                 },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1629,9 +2356,6 @@ var doc = `{
                 "status": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "vips": {
                     "type": "string"
                 }
@@ -1649,15 +2373,6 @@ var doc = `{
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -1669,9 +2384,6 @@ var doc = `{
                 },
                 "securityGroupID": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1681,26 +2393,14 @@ var doc = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "domain": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
                 "proxy_domain": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1713,18 +2413,9 @@ var doc = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "email": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1739,8 +2430,38 @@ var doc = `{
                 },
                 "state": {
                     "type": "string"
+                }
+            }
+        },
+        "rbac.Permission": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
+                },
+                "object": {
+                    "type": "string"
+                }
+            }
+        },
+        "rbac.RoleManager": {
+            "type": "object"
+        },
+        "types.AddPermissionForRoleRequest": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
                 },
-                "updated_at": {
+                "object": {
                     "type": "string"
                 }
             }
@@ -1801,6 +2522,31 @@ var doc = `{
                 }
             }
         },
+        "types.CreateOauthRequest": {
+            "type": "object",
+            "required": [
+                "client_id",
+                "client_secret",
+                "name"
+            ],
+            "properties": {
+                "bio": {
+                    "type": "string"
+                },
+                "client_id": {
+                    "type": "string"
+                },
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
         "types.CreatePreheatRequest": {
             "type": "object",
             "required": [
@@ -1828,6 +2574,24 @@ var doc = `{
                 }
             }
         },
+        "types.CreateRoleRequest": {
+            "type": "object",
+            "required": [
+                "permissions",
+                "role"
+            ],
+            "properties": {
+                "permissions": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/rbac.Permission"
+                    }
+                },
+                "role": {
+                    "type": "string"
+                }
+            }
+        },
         "types.CreateSchedulerClusterRequest": {
             "type": "object",
             "required": [
@@ -1923,6 +2687,21 @@ var doc = `{
                 }
             }
         },
+        "types.DeletePermissionForRoleRequest": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
+                },
+                "object": {
+                    "type": "string"
+                }
+            }
+        },
         "types.Preheat": {
             "type": "object",
             "properties": {
@@ -1937,6 +2716,21 @@ var doc = `{
                 }
             }
         },
+        "types.ResetPasswordRequest": {
+            "type": "object",
+            "required": [
+                "new_password",
+                "old_password"
+            ],
+            "properties": {
+                "new_password": {
+                    "type": "string"
+                },
+                "old_password": {
+                    "type": "string"
+                }
+            }
+        },
         "types.SignUpRequest": {
             "type": "object",
             "required": [
@@ -2009,6 +2803,26 @@ var doc = `{
                 }
             }
         },
+        "types.UpdateOauthRequest": {
+            "type": "object",
+            "properties": {
+                "bio": {
+                    "type": "string"
+                },
+                "client_id": {
+                    "type": "string"
+                },
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
         "types.UpdateSchedulerClusterRequest": {
             "type": "object",
             "properties": {
diff --git a/api/manager/swagger.json b/api/manager/swagger.json
index 67f7b76b..53097649 100644
--- a/api/manager/swagger.json
+++ b/api/manager/swagger.json
@@ -575,9 +575,472 @@
                 }
             }
         },
+        "/oauth": {
+            "get": {
+                "description": "Get Oauths",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Get Oauths",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "default": 0,
+                        "description": "current page",
+                        "name": "page",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "maximum": 50,
+                        "minimum": 2,
+                        "type": "integer",
+                        "default": 10,
+                        "description": "return max item count, default 10, max 50",
+                        "name": "per_page",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/model.Oauth"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "create by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Create Oauth",
+                "parameters": [
+                    {
+                        "description": "Oauth",
+                        "name": "Oauth",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateOauthRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/oauth/{id}": {
+            "get": {
+                "description": "Get Oauth by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Get Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Destroy Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Update Oauth",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Oauth",
+                        "name": "Oauth",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateOauthRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.Oauth"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/permissions": {
+            "get": {
+                "description": "Get Permissions",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Permission"
+                ],
+                "summary": "Get Permissions",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/rbac.Permission"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
         "/preheats": {
             "post": {
-                "description": "create by json config",
+                "description": "create by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Preheat"
+                ],
+                "summary": "Create Preheat",
+                "parameters": [
+                    {
+                        "description": "Preheat",
+                        "name": "CDN",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreatePreheatRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Preheat"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/preheats/{id}": {
+            "get": {
+                "description": "Get Preheat by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Preheat"
+                ],
+                "summary": "Get Preheat",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Preheat"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles": {
+            "get": {
+                "description": "Get roles",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Get Roles",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create Role by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Create Role",
+                "parameters": [
+                    {
+                        "description": "Role",
+                        "name": "Role",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateRoleRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles/:role": {
+            "get": {
+                "description": "Get Role",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Get Role",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy role by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Role"
+                ],
+                "summary": "Destroy Role",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/roles/:role/permissions": {
+            "post": {
+                "description": "Add Permission by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -585,42 +1048,41 @@
                     "application/json"
                 ],
                 "tags": [
-                    "Preheat"
+                    "Role"
                 ],
-                "summary": "Create Preheat",
+                "summary": "Add Permission For Role",
                 "parameters": [
                     {
-                        "description": "Preheat",
-                        "name": "CDN",
+                        "description": "Permission",
+                        "name": "Permission",
                         "in": "body",
                         "required": true,
                         "schema": {
-                            "$ref": "#/definitions/types.CreatePreheatRequest"
+                            "$ref": "#/definitions/types.AddPermissionForRoleRequest"
                         }
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/types.Preheat"
-                        }
-                    },
-                    "400": {
                         "description": ""
                     },
-                    "404": {
+                    "400": {
                         "description": ""
                     },
                     "500": {
                         "description": ""
                     }
                 }
-            }
-        },
-        "/preheats/{id}": {
-            "get": {
-                "description": "Get Preheat by id",
+            },
+            "delete": {
+                "description": "Remove Role Permission by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -628,29 +1090,32 @@
                     "application/json"
                 ],
                 "tags": [
-                    "Preheat"
+                    "Role"
                 ],
-                "summary": "Get Preheat",
+                "summary": "Update Role",
                 "parameters": [
+                    {
+                        "description": "Permission",
+                        "name": "Permission",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.DeletePermissionForRoleRequest"
+                        }
+                    },
                     {
                         "type": "string",
-                        "description": "id",
-                        "name": "id",
+                        "description": "role",
+                        "name": "role",
                         "in": "path",
                         "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/types.Preheat"
-                        }
-                    },
-                    "400": {
                         "description": ""
                     },
-                    "404": {
+                    "400": {
                         "description": ""
                     },
                     "500": {
@@ -1367,28 +1832,262 @@
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/security-groups/{id}/scheduler-clusters/{scheduler_cluster_id}": {
+            "put": {
+                "description": "Add Scheduler to SecurityGroup",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SecurityGroup"
+                ],
+                "summary": "Add Scheduler to SecurityGroup",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "scheduler cluster id",
+                        "name": "scheduler_cluster_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/securityGroups/{id}": {
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SecurityGroup"
+                ],
+                "summary": "Destroy SecurityGroup",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signin/{name}": {
+            "get": {
+                "description": "oauth signin by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Oauth Signin",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "name",
+                        "name": "name",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signin/{name}/callback": {
+            "get": {
+                "description": "oauth signin callback by json config",
+                "tags": [
+                    "Oauth"
+                ],
+                "summary": "Oauth Signin Callback",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "name",
+                        "name": "name",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "code",
+                        "name": "code",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/user/signup": {
+            "post": {
+                "description": "signup by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "SignUp user",
+                "parameters": [
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.SignUpRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.User"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/users/:id/reset_password": {
+            "post": {
+                "description": "reset password by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Reset Password For User",
+                "parameters": [
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.ResetPasswordRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
                     "500": {
                         "description": ""
                     }
                 }
             }
         },
-        "/security-groups/{id}/scheduler-clusters/{scheduler_cluster_id}": {
-            "put": {
-                "description": "Add Scheduler to SecurityGroup",
-                "consumes": [
-                    "application/json"
-                ],
+        "/users/:id/roles": {
+            "get": {
+                "description": "get roles by json config",
                 "produces": [
                     "application/json"
                 ],
                 "tags": [
-                    "SecurityGroup"
+                    "User"
                 ],
-                "summary": "Add Scheduler to SecurityGroup",
+                "summary": "Get User Roles",
                 "parameters": [
                     {
                         "type": "string",
@@ -1396,34 +2095,30 @@
                         "name": "id",
                         "in": "path",
                         "required": true
-                    },
-                    {
-                        "type": "string",
-                        "description": "scheduler cluster id",
-                        "name": "scheduler_cluster_id",
-                        "in": "path",
-                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": ""
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            }
+                        }
                     },
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
                     "500": {
                         "description": ""
                     }
                 }
             }
         },
-        "/securityGroups/{id}": {
-            "delete": {
-                "description": "Destroy by id",
+        "/users/:id/roles/:role": {
+            "put": {
+                "description": "add role to user by uri config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1431,9 +2126,9 @@
                     "application/json"
                 ],
                 "tags": [
-                    "SecurityGroup"
+                    "Users"
                 ],
-                "summary": "Destroy SecurityGroup",
+                "summary": "Add Role For User",
                 "parameters": [
                     {
                         "type": "string",
@@ -1441,6 +2136,13 @@
                         "name": "id",
                         "in": "path",
                         "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
@@ -1450,18 +2152,13 @@
                     "400": {
                         "description": ""
                     },
-                    "404": {
-                        "description": ""
-                    },
                     "500": {
                         "description": ""
                     }
                 }
-            }
-        },
-        "/user/signup": {
-            "post": {
-                "description": "SignUp user by json config",
+            },
+            "delete": {
+                "description": "delete role by uri config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1469,26 +2166,28 @@
                     "application/json"
                 ],
                 "tags": [
-                    "User"
+                    "Users"
                 ],
-                "summary": "SignUp user",
+                "summary": "Delete Role For User",
                 "parameters": [
                     {
-                        "description": "User",
-                        "name": "User",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "$ref": "#/definitions/types.SignUpRequest"
-                        }
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "role",
+                        "name": "role",
+                        "in": "path",
+                        "required": true
                     }
                 ],
                 "responses": {
                     "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/model.User"
-                        }
+                        "description": ""
                     },
                     "400": {
                         "description": ""
@@ -1501,33 +2200,65 @@
         }
     },
     "definitions": {
+        "model.Assertion": {
+            "type": "object",
+            "properties": {
+                "key": {
+                    "type": "string"
+                },
+                "policy": {
+                    "type": "array",
+                    "items": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    }
+                },
+                "policyMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "rm": {
+                    "$ref": "#/definitions/rbac.RoleManager"
+                },
+                "tokens": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "value": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.AssertionMap": {
+            "type": "object",
+            "additionalProperties": {
+                "$ref": "#/definitions/model.Assertion"
+            }
+        },
         "model.CDN": {
             "type": "object",
             "properties": {
                 "cdnclusterID": {
                     "type": "integer"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "download_port": {
                     "type": "integer"
                 },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
                 "ip": {
                     "type": "string"
                 },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1536,9 +2267,6 @@
                 },
                 "status": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1551,15 +2279,6 @@
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -1568,9 +2287,6 @@
                 },
                 "securityGroupID": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1578,17 +2294,31 @@
             "type": "object",
             "additionalProperties": true
         },
-        "model.Scheduler": {
+        "model.Oauth": {
             "type": "object",
             "properties": {
-                "created_at": {
+                "bio": {
                     "type": "string"
                 },
-                "host_name": {
+                "client_id": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.Scheduler": {
+            "type": "object",
+            "properties": {
+                "host_name": {
+                    "type": "string"
                 },
                 "idc": {
                     "type": "string"
@@ -1596,9 +2326,6 @@
                 "ip": {
                     "type": "string"
                 },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1614,9 +2341,6 @@
                 "status": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "vips": {
                     "type": "string"
                 }
@@ -1634,15 +2358,6 @@
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -1654,9 +2369,6 @@
                 },
                 "securityGroupID": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1666,26 +2378,14 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "domain": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
                 "proxy_domain": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -1698,18 +2398,9 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "email": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
-                "isDel": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -1724,8 +2415,38 @@
                 },
                 "state": {
                     "type": "string"
+                }
+            }
+        },
+        "rbac.Permission": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
+                },
+                "object": {
+                    "type": "string"
+                }
+            }
+        },
+        "rbac.RoleManager": {
+            "type": "object"
+        },
+        "types.AddPermissionForRoleRequest": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
                 },
-                "updated_at": {
+                "object": {
                     "type": "string"
                 }
             }
@@ -1786,6 +2507,31 @@
                 }
             }
         },
+        "types.CreateOauthRequest": {
+            "type": "object",
+            "required": [
+                "client_id",
+                "client_secret",
+                "name"
+            ],
+            "properties": {
+                "bio": {
+                    "type": "string"
+                },
+                "client_id": {
+                    "type": "string"
+                },
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
         "types.CreatePreheatRequest": {
             "type": "object",
             "required": [
@@ -1813,6 +2559,24 @@
                 }
             }
         },
+        "types.CreateRoleRequest": {
+            "type": "object",
+            "required": [
+                "permissions",
+                "role"
+            ],
+            "properties": {
+                "permissions": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/rbac.Permission"
+                    }
+                },
+                "role": {
+                    "type": "string"
+                }
+            }
+        },
         "types.CreateSchedulerClusterRequest": {
             "type": "object",
             "required": [
@@ -1908,6 +2672,21 @@
                 }
             }
         },
+        "types.DeletePermissionForRoleRequest": {
+            "type": "object",
+            "required": [
+                "action",
+                "object"
+            ],
+            "properties": {
+                "action": {
+                    "type": "string"
+                },
+                "object": {
+                    "type": "string"
+                }
+            }
+        },
         "types.Preheat": {
             "type": "object",
             "properties": {
@@ -1922,6 +2701,21 @@
                 }
             }
         },
+        "types.ResetPasswordRequest": {
+            "type": "object",
+            "required": [
+                "new_password",
+                "old_password"
+            ],
+            "properties": {
+                "new_password": {
+                    "type": "string"
+                },
+                "old_password": {
+                    "type": "string"
+                }
+            }
+        },
         "types.SignUpRequest": {
             "type": "object",
             "required": [
@@ -1994,6 +2788,26 @@
                 }
             }
         },
+        "types.UpdateOauthRequest": {
+            "type": "object",
+            "properties": {
+                "bio": {
+                    "type": "string"
+                },
+                "client_id": {
+                    "type": "string"
+                },
+                "client_secret": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "redirect_url": {
+                    "type": "string"
+                }
+            }
+        },
         "types.UpdateSchedulerClusterRequest": {
             "type": "object",
             "properties": {
diff --git a/api/manager/swagger.yaml b/api/manager/swagger.yaml
index 873142b6..5c4434ee 100644
--- a/api/manager/swagger.yaml
+++ b/api/manager/swagger.yaml
@@ -1,31 +1,50 @@
 basePath: /api/v1
 definitions:
+  model.Assertion:
+    properties:
+      key:
+        type: string
+      policy:
+        items:
+          items:
+            type: string
+          type: array
+        type: array
+      policyMap:
+        additionalProperties:
+          type: integer
+        type: object
+      rm:
+        $ref: '#/definitions/rbac.RoleManager'
+      tokens:
+        items:
+          type: string
+        type: array
+      value:
+        type: string
+    type: object
+  model.AssertionMap:
+    additionalProperties:
+      $ref: '#/definitions/model.Assertion'
+    type: object
   model.CDN:
     properties:
       cdnclusterID:
         type: integer
-      created_at:
-        type: string
       download_port:
         type: integer
       host_name:
         type: string
-      id:
-        type: integer
       idc:
         type: string
       ip:
         type: string
-      isDel:
-        type: integer
       location:
         type: string
       port:
         type: integer
       status:
         type: string
-      updated_at:
-        type: string
     type: object
   model.CDNCluster:
     properties:
@@ -33,38 +52,37 @@ definitions:
         type: string
       config:
         $ref: '#/definitions/model.JSONMap'
-      created_at:
-        type: string
-      id:
-        type: integer
       is_default:
         type: boolean
-      isDel:
-        type: integer
       name:
         type: string
       securityGroupID:
         type: integer
-      updated_at:
-        type: string
     type: object
   model.JSONMap:
     additionalProperties: true
     type: object
-  model.Scheduler:
+  model.Oauth:
     properties:
-      created_at:
+      bio:
+        type: string
+      client_id:
+        type: string
+      client_secret:
+        type: string
+      name:
+        type: string
+      redirect_url:
         type: string
+    type: object
+  model.Scheduler:
+    properties:
       host_name:
         type: string
-      id:
-        type: integer
       idc:
         type: string
       ip:
         type: string
-      isDel:
-        type: integer
       location:
         type: string
       net_config:
@@ -75,8 +93,6 @@ definitions:
         type: integer
       status:
         type: string
-      updated_at:
-        type: string
       vips:
         type: string
     type: object
@@ -88,41 +104,25 @@ definitions:
         $ref: '#/definitions/model.JSONMap'
       config:
         $ref: '#/definitions/model.JSONMap'
-      created_at:
-        type: string
-      id:
-        type: integer
       is_default:
         type: boolean
-      isDel:
-        type: integer
       name:
         type: string
       scopes:
         $ref: '#/definitions/model.JSONMap'
       securityGroupID:
         type: integer
-      updated_at:
-        type: string
     type: object
   model.SecurityGroup:
     properties:
       bio:
         type: string
-      created_at:
-        type: string
       domain:
         type: string
-      id:
-        type: integer
-      isDel:
-        type: integer
       name:
         type: string
       proxy_domain:
         type: string
-      updated_at:
-        type: string
     type: object
   model.User:
     properties:
@@ -130,14 +130,8 @@ definitions:
         type: string
       bio:
         type: string
-      created_at:
-        type: string
       email:
         type: string
-      id:
-        type: integer
-      isDel:
-        type: integer
       location:
         type: string
       name:
@@ -148,8 +142,28 @@ definitions:
         type: string
       state:
         type: string
-      updated_at:
+    type: object
+  rbac.Permission:
+    properties:
+      action:
+        type: string
+      object:
+        type: string
+    required:
+    - action
+    - object
+    type: object
+  rbac.RoleManager:
+    type: object
+  types.AddPermissionForRoleRequest:
+    properties:
+      action:
+        type: string
+      object:
         type: string
+    required:
+    - action
+    - object
     type: object
   types.CreateCDNClusterRequest:
     properties:
@@ -190,6 +204,23 @@ definitions:
     - ip
     - port
     type: object
+  types.CreateOauthRequest:
+    properties:
+      bio:
+        type: string
+      client_id:
+        type: string
+      client_secret:
+        type: string
+      name:
+        type: string
+      redirect_url:
+        type: string
+    required:
+    - client_id
+    - client_secret
+    - name
+    type: object
   types.CreatePreheatRequest:
     properties:
       filter:
@@ -208,6 +239,18 @@ definitions:
     - type
     - url
     type: object
+  types.CreateRoleRequest:
+    properties:
+      permissions:
+        items:
+          $ref: '#/definitions/rbac.Permission'
+        type: array
+      role:
+        type: string
+    required:
+    - permissions
+    - role
+    type: object
   types.CreateSchedulerClusterRequest:
     properties:
       bio:
@@ -274,6 +317,16 @@ definitions:
     - domain
     - name
     type: object
+  types.DeletePermissionForRoleRequest:
+    properties:
+      action:
+        type: string
+      object:
+        type: string
+    required:
+    - action
+    - object
+    type: object
   types.Preheat:
     properties:
       create_at:
@@ -283,6 +336,16 @@ definitions:
       status:
         type: string
     type: object
+  types.ResetPasswordRequest:
+    properties:
+      new_password:
+        type: string
+      old_password:
+        type: string
+    required:
+    - new_password
+    - old_password
+    type: object
   types.SignUpRequest:
     properties:
       avatar:
@@ -331,6 +394,19 @@ definitions:
       port:
         type: integer
     type: object
+  types.UpdateOauthRequest:
+    properties:
+      bio:
+        type: string
+      client_id:
+        type: string
+      client_secret:
+        type: string
+      name:
+        type: string
+      redirect_url:
+        type: string
+    type: object
   types.UpdateSchedulerClusterRequest:
     properties:
       bio:
@@ -769,6 +845,173 @@ paths:
       summary: Get Health
       tags:
       - Health
+  /oauth:
+    get:
+      consumes:
+      - application/json
+      description: Get Oauths
+      parameters:
+      - default: 0
+        description: current page
+        in: query
+        name: page
+        required: true
+        type: integer
+      - default: 10
+        description: return max item count, default 10, max 50
+        in: query
+        maximum: 50
+        minimum: 2
+        name: per_page
+        required: true
+        type: integer
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              $ref: '#/definitions/model.Oauth'
+            type: array
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Oauths
+      tags:
+      - Oauth
+    post:
+      consumes:
+      - application/json
+      description: create by json config
+      parameters:
+      - description: Oauth
+        in: body
+        name: Oauth
+        required: true
+        schema:
+          $ref: '#/definitions/types.CreateOauthRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/model.Oauth'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Create Oauth
+      tags:
+      - Oauth
+  /oauth/{id}:
+    delete:
+      consumes:
+      - application/json
+      description: Destroy by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Destroy Oauth
+      tags:
+      - Oauth
+    get:
+      consumes:
+      - application/json
+      description: Get Oauth by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/model.Oauth'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Oauth
+      tags:
+      - Oauth
+    patch:
+      consumes:
+      - application/json
+      description: Update by json config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: Oauth
+        in: body
+        name: Oauth
+        required: true
+        schema:
+          $ref: '#/definitions/types.UpdateOauthRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/model.Oauth'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Update Oauth
+      tags:
+      - Oauth
+  /permissions:
+    get:
+      description: Get Permissions
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              $ref: '#/definitions/rbac.Permission'
+            type: array
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Permissions
+      tags:
+      - Permission
   /preheats:
     post:
       consumes:
@@ -824,6 +1067,148 @@ paths:
       summary: Get Preheat
       tags:
       - Preheat
+  /roles:
+    get:
+      consumes:
+      - application/json
+      description: Get roles
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Roles
+      tags:
+      - Role
+    post:
+      consumes:
+      - application/json
+      description: Create Role by json config
+      parameters:
+      - description: Role
+        in: body
+        name: Role
+        required: true
+        schema:
+          $ref: '#/definitions/types.CreateRoleRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Create Role
+      tags:
+      - Role
+  /roles/:role:
+    delete:
+      consumes:
+      - application/json
+      description: Destroy role by json config
+      parameters:
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Destroy Role
+      tags:
+      - Role
+    get:
+      consumes:
+      - application/json
+      description: Get Role
+      parameters:
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Role
+      tags:
+      - Role
+  /roles/:role/permissions:
+    delete:
+      consumes:
+      - application/json
+      description: Remove Role Permission by json config
+      parameters:
+      - description: Permission
+        in: body
+        name: Permission
+        required: true
+        schema:
+          $ref: '#/definitions/types.DeletePermissionForRoleRequest'
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Update Role
+      tags:
+      - Role
+    post:
+      consumes:
+      - application/json
+      description: Add Permission by json config
+      parameters:
+      - description: Permission
+        in: body
+        name: Permission
+        required: true
+        schema:
+          $ref: '#/definitions/types.AddPermissionForRoleRequest'
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Add Permission For Role
+      tags:
+      - Role
   /scheduler-clusters:
     get:
       consumes:
@@ -1359,11 +1744,62 @@ paths:
       summary: Destroy SecurityGroup
       tags:
       - SecurityGroup
+  /user/signin/{name}:
+    get:
+      consumes:
+      - application/json
+      description: oauth signin by json config
+      parameters:
+      - description: name
+        in: path
+        name: name
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Oauth Signin
+      tags:
+      - User
+  /user/signin/{name}/callback:
+    get:
+      description: oauth signin callback by json config
+      parameters:
+      - description: name
+        in: path
+        name: name
+        required: true
+        type: string
+      - description: code
+        in: query
+        name: code
+        required: true
+        type: string
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Oauth Signin Callback
+      tags:
+      - Oauth
   /user/signup:
     post:
       consumes:
       - application/json
-      description: SignUp user by json config
+      description: signup by json config
       parameters:
       - description: User
         in: body
@@ -1385,4 +1821,108 @@ paths:
       summary: SignUp user
       tags:
       - User
+  /users/:id/reset_password:
+    post:
+      consumes:
+      - application/json
+      description: reset password by json config
+      parameters:
+      - description: User
+        in: body
+        name: User
+        required: true
+        schema:
+          $ref: '#/definitions/types.ResetPasswordRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Reset Password For User
+      tags:
+      - User
+  /users/:id/roles:
+    get:
+      description: get roles by json config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              type: string
+            type: array
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Get User Roles
+      tags:
+      - User
+  /users/:id/roles/:role:
+    delete:
+      consumes:
+      - application/json
+      description: delete role by uri config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Delete Role For User
+      tags:
+      - Users
+    put:
+      consumes:
+      - application/json
+      description: add role to user by uri config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: role
+        in: path
+        name: role
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "500":
+          description: ""
+      summary: Add Role For User
+      tags:
+      - Users
 swagger: "2.0"
diff --git a/go.mod b/go.mod
index ff7fea47..9110d3f0 100644
--- a/go.mod
+++ b/go.mod
@@ -31,6 +31,8 @@ require (
 	github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
 	github.com/golang/mock v1.6.0
 	github.com/golang/protobuf v1.5.2
+	github.com/google/go-github v17.0.0+incompatible
+	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/uuid v1.2.0
 	github.com/gorilla/mux v1.7.3
 	github.com/jarcoal/httpmock v1.0.8
@@ -73,6 +75,7 @@ require (
 	golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
 	golang.org/x/tools v0.1.4 // indirect
 	gonum.org/v1/gonum v0.9.3
+	google.golang.org/api v0.39.0
 	google.golang.org/grpc v1.39.0
 	google.golang.org/protobuf v1.26.0
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
@@ -81,6 +84,6 @@ require (
 	gorm.io/driver/mysql v1.0.5
 	gorm.io/gorm v1.21.9
 	gorm.io/plugin/soft_delete v1.0.2
-	k8s.io/apimachinery v0.20.6 // indirect
+	k8s.io/apimachinery v0.20.6
 	k8s.io/client-go v11.0.0+incompatible
 )
diff --git a/go.sum b/go.sum
index ec9f21f6..7eeb831a 100644
--- a/go.sum
+++ b/go.sum
@@ -341,6 +341,10 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
diff --git a/manager/auth/oauth/github.go b/manager/auth/oauth/github.go
index cdd46709..d0cd783f 100644
--- a/manager/auth/oauth/github.go
+++ b/manager/auth/oauth/github.go
@@ -1,59 +1,78 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package oauth
 
 import (
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"strings"
+	"context"
+	"crypto/rand"
+	"encoding/base64"
 
+	"github.com/google/go-github/github"
 	"golang.org/x/oauth2"
-	"golang.org/x/oauth2/github"
-	"gorm.io/gorm"
+	oauth2github "golang.org/x/oauth2/github"
 )
 
-type githubOauth2 struct {
-	baseOauth2
+var githubScopes = []string{
+	"user",
+	"public_repo",
 }
 
-func NewGithubOauth2(name string, clientID string, clientSecret string, db *gorm.DB) (Oauther, error) {
+type oauthGithub struct {
+	*oauth2.Config
+}
 
-	oa := &githubOauth2{}
-	oa.Name = name
-	oa.Config = &oauth2.Config{
-		ClientID:     clientID,
-		ClientSecret: clientSecret,
-		Scopes:       strings.Split(GithubScopes, ","),
-		Endpoint:     github.Endpoint,
+func newGithub(name, clientID, clientSecret, redirectURL string) *oauthGithub {
+	return &oauthGithub{
+		Config: &oauth2.Config{
+			ClientID:     clientID,
+			ClientSecret: clientSecret,
+			Scopes:       githubScopes,
+			Endpoint:     oauth2github.Endpoint,
+			RedirectURL:  redirectURL,
+		},
 	}
-	oa.UserInfoURL = GithubUserInfoURL
+}
 
-	redirectURL, err := oa.GetRediectURL(db)
-	if err != nil {
-		return nil, err
-	}
-	oa.Config.RedirectURL = redirectURL
-	return oa, nil
+func (g *oauthGithub) AuthCodeURL() string {
+	b := make([]byte, 16)
+	rand.Read(b)
+	return g.Config.AuthCodeURL(base64.URLEncoding.EncodeToString(b))
 }
 
-func (oa *githubOauth2) GetOauthUserInfo(token string) (*oauth2User, error) {
-	req, err := http.NewRequest("GET", oa.UserInfoURL, nil)
-	if err != nil {
-		return nil, err
-	}
-	req.Header.Set("Authorization", "token"+" "+token)
-	response, err := (&http.Client{}).Do(req)
-	if err != nil {
-		return nil, err
-	}
-	defer response.Body.Close()
-	contents, err := ioutil.ReadAll(response.Body)
-	if err != nil {
-		return nil, err
-	}
-	u := oauth2User{}
-	err = json.Unmarshal(contents, &u)
+func (g *oauthGithub) Exchange(code string) (*oauth2.Token, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	return g.Config.Exchange(ctx, code)
+}
+
+func (g *oauthGithub) GetUser(token *oauth2.Token) (*User, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	client := github.NewClient(g.Client(ctx, token))
+	user, _, err := client.Users.Get(ctx, "")
 	if err != nil {
 		return nil, err
 	}
-	return &u, nil
+
+	return &User{
+		Name:   *user.Name,
+		Email:  *user.Email,
+		Avatar: *user.AvatarURL,
+	}, nil
 }
diff --git a/manager/auth/oauth/google.go b/manager/auth/oauth/google.go
index 90bd61a3..48ed98c1 100644
--- a/manager/auth/oauth/google.go
+++ b/manager/auth/oauth/google.go
@@ -1,33 +1,83 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package oauth
 
 import (
-	"strings"
+	"context"
+	"crypto/rand"
+	"encoding/base64"
 
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/google"
-	"gorm.io/gorm"
+	oauth2v2 "google.golang.org/api/oauth2/v2"
+	"google.golang.org/api/option"
 )
 
-type googleOauth2 struct {
-	baseOauth2
+var googleScopes = []string{
+	"https://www.googleapis.com/auth/userinfo.email",
+	"https://www.googleapis.com/auth/userinfo.profile",
+}
+
+type oauthGoogle struct {
+	*oauth2.Config
+}
+
+func newGoogle(name, clientID, clientSecret, redirectURL string) *oauthGoogle {
+	return &oauthGoogle{
+		Config: &oauth2.Config{
+			ClientID:     clientID,
+			ClientSecret: clientSecret,
+			Scopes:       googleScopes,
+			Endpoint:     google.Endpoint,
+			RedirectURL:  redirectURL,
+		},
+	}
+}
+
+func (g *oauthGoogle) AuthCodeURL() string {
+	b := make([]byte, 16)
+	rand.Read(b)
+	return g.Config.AuthCodeURL(base64.URLEncoding.EncodeToString(b))
+}
+
+func (g *oauthGoogle) Exchange(code string) (*oauth2.Token, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	return g.Config.Exchange(ctx, code)
 }
 
-func NewGoogleOauth2(name string, clientID string, clientSecret string, db *gorm.DB) (Oauther, error) {
+func (g *oauthGoogle) GetUser(token *oauth2.Token) (*User, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
 
-	oa := &googleOauth2{}
-	oa.Name = name
-	oa.Config = &oauth2.Config{
-		ClientID:     clientID,
-		ClientSecret: clientSecret,
-		Scopes:       strings.Split(GoogleScopes, ","),
-		Endpoint:     google.Endpoint,
+	client, err := oauth2v2.NewService(ctx, option.WithTokenSource(g.Config.TokenSource(ctx, token)))
+	if err != nil {
+		return nil, err
 	}
-	oa.UserInfoURL = GithubUserInfoURL
 
-	redirectURL, err := oa.GetRediectURL(db)
+	user, err := client.Userinfo.Get().Do()
 	if err != nil {
 		return nil, err
 	}
-	oa.Config.RedirectURL = redirectURL
-	return oa, nil
+
+	return &User{
+		Name:   user.Name,
+		Email:  user.Email,
+		Avatar: user.Picture,
+	}, nil
 }
diff --git a/manager/auth/oauth/oauth.go b/manager/auth/oauth/oauth.go
index 9eb38b43..a832a3fe 100644
--- a/manager/auth/oauth/oauth.go
+++ b/manager/auth/oauth/oauth.go
@@ -1,164 +1,75 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package oauth
 
 import (
-	"context"
-	"encoding/json"
 	"errors"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"strings"
+	"time"
 
-	"d7y.io/dragonfly/v2/manager/model"
-	"d7y.io/dragonfly/v2/pkg/util/stringutils"
-	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/oauth2"
-	"gorm.io/gorm"
 )
 
 const (
-	Google            = "google"
-	Github            = "github"
-	GoogleScopes      = "https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile"
-	GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo"
+	timeout = 5 * time.Second
+)
 
-	GithubScopes      = "user,public_repo"
-	GithubUserInfoURL = "https://api.github.com/user"
+const (
+	Google = "google"
+	Github = "github"
 )
 
-type baseOauth2 struct {
-	Name        string
-	UserInfoURL string
-	Config      *oauth2.Config
+type User struct {
+	Name   string
+	Email  string
+	Avatar string
 }
 
-type oauth2User struct {
-	Name  string `json:"name"`
-	Email string `json:"email"`
+type Oauth interface {
+	AuthCodeURL() string
+	Exchange(string) (*oauth2.Token, error)
+	GetUser(*oauth2.Token) (*User, error)
 }
 
-// oauth interface
-type Oauther interface {
-	GetRediectURL(*gorm.DB) (string, error)
-	GetOauthUserInfo(string) (*oauth2User, error)
-	ExchangeTokenByCode(string) (string, error)
-	OauthLinkUser(*oauth2User, *gorm.DB) (*model.User, error)
-	AuthCodeURL() string
+type oauth struct {
+	Oauth Oauth
 }
 
-func New(oauth *model.Oauth, db *gorm.DB) (Oauther, error) {
-	var o Oauther
-	var err error
-	switch oauth.Name {
+func New(name, clientID, clientSecret, redirectURL string) (Oauth, error) {
+	var o Oauth
+	switch name {
 	case Google:
-		o, err = NewGoogleOauth2(oauth.Name, oauth.ClientID, oauth.ClientSecret, db)
+		o = newGoogle(name, clientID, clientSecret, redirectURL)
 	case Github:
-		o, err = NewGithubOauth2(oauth.Name, oauth.ClientID, oauth.ClientSecret, db)
+		o = newGithub(name, clientID, clientSecret, redirectURL)
 	default:
-		o, err = NewBaseOauth2(oauth.Name, oauth.ClientID, oauth.ClientSecret, oauth.Scopes, oauth.AuthURL, oauth.TokenURL, db)
-	}
-	if err != nil {
-		return nil, err
-	}
-	return o, nil
-
-}
-
-func NewBaseOauth2(name string, clientID string, clientSecret string, scopes string, authURL string, tokenURL string, db *gorm.DB) (Oauther, error) {
-
-	oa := &baseOauth2{
-		Name: name,
-		Config: &oauth2.Config{
-			ClientID:     clientID,
-			ClientSecret: clientSecret,
-			Scopes:       strings.Split(scopes, ","),
-			Endpoint: oauth2.Endpoint{
-				AuthURL:  authURL,
-				TokenURL: tokenURL,
-			},
-		},
-	}
-	redirectURL, err := oa.GetRediectURL(db)
-	if err != nil {
-		return nil, err
-	}
-	oa.Config.RedirectURL = redirectURL
-	return oa, nil
-}
-
-func (oa *baseOauth2) GetRediectURL(db *gorm.DB) (string, error) {
-
-	s := model.Settings{}
-	if err := db.First(&s, model.Settings{
-		Key: "server_domain",
-	}).Error; err != nil {
-		return "", err
+		return nil, errors.New("invalid oauth name")
 	}
-	return fmt.Sprintf("%s/api/v1/oauth/callback/%s", s.Value, oa.Name), nil
-}
 
-func (oa *baseOauth2) AuthCodeURL() string {
-	return oa.Config.AuthCodeURL(stringutils.RandString(5))
+	return o, nil
 }
 
-func (oa *baseOauth2) GetOauthUserInfo(token string) (*oauth2User, error) {
-	response, err := http.Get(fmt.Sprintf("%s?access_token=%s", oa.UserInfoURL, token))
-	if err != nil {
-		return nil, err
-	}
-	defer response.Body.Close()
-	contents, err := ioutil.ReadAll(response.Body)
-	if err != nil {
-		return nil, err
-	}
-	u := oauth2User{}
-	err = json.Unmarshal(contents, &u)
-	if err != nil {
-		return nil, err
-	}
-	return &u, nil
+func (g *oauth) AuthCodeURL() string {
+	return g.Oauth.AuthCodeURL()
 }
 
-func (oa *baseOauth2) ExchangeTokenByCode(code string) (string, error) {
-	token, err := oa.Config.Exchange(context.Background(), code)
-	if err != nil {
-		return "", err
-	}
-	if oa.UserInfoURL == "" {
-		return "", errors.New("UserInfoURL is empty")
-	}
-	return token.AccessToken, nil
+func (g *oauth) Exchange(code string) (*oauth2.Token, error) {
+	return g.Oauth.Exchange(code)
 }
 
-func (oa *baseOauth2) OauthLinkUser(u *oauth2User, db *gorm.DB) (*model.User, error) {
-	if u.Name == "admin" {
-		return nil, errors.New("admin is not allowed to login by oauth")
-	}
-	encryptedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte("Dragonfly2"), bcrypt.MinCost)
-	if err != nil {
-		return nil, err
-	}
-	var userCount int64
-	if err := db.Model(model.User{}).Where("name = ?", u.Name).Count(&userCount).Error; err != nil {
-		return nil, err
-	}
-	if userCount <= 0 {
-		user := model.User{
-			EncryptedPassword: string(encryptedPasswordBytes),
-			Name:              u.Name,
-			Email:             u.Email,
-		}
-
-		if err := db.Create(&user).Error; err != nil {
-			return nil, err
-		}
-		return &user, nil
-
-	}
-	user := model.User{}
-	if err := db.Model(model.User{}).Where("name = ?", u.Name).First(&user).Error; err != nil {
-		return nil, err
-	}
-	return &user, nil
-
+func (g *oauth) GetUser(token *oauth2.Token) (*User, error) {
+	return g.Oauth.GetUser(token)
 }
diff --git a/manager/database/database.go b/manager/database/database.go
index 1b7be26b..0f4ff627 100644
--- a/manager/database/database.go
+++ b/manager/database/database.go
@@ -23,7 +23,6 @@ import (
 	"d7y.io/dragonfly/v2/manager/config"
 	"d7y.io/dragonfly/v2/manager/model"
 	"github.com/go-redis/redis/v8"
-	"golang.org/x/crypto/bcrypt"
 	"gorm.io/driver/mysql"
 	"gorm.io/gorm"
 	"gorm.io/gorm/schema"
@@ -101,7 +100,6 @@ func migrate(db *gorm.DB) error {
 		&model.Scheduler{},
 		&model.SecurityGroup{},
 		&model.User{},
-		&model.Settings{},
 		&model.Oauth{},
 	)
 }
@@ -158,26 +156,5 @@ func seed(db *gorm.DB) error {
 		}
 	}
 
-	var rootUserCount int64
-	if err := db.Model(model.User{}).Count(&rootUserCount).Error; err != nil {
-		return err
-	}
-	if rootUserCount <= 0 {
-		encryptedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte("dragonfly"), bcrypt.MinCost)
-		if err != nil {
-			return err
-		}
-		if err := db.Create(&model.User{
-			Model: model.Model{
-				ID: uint(1),
-			},
-			EncryptedPassword: string(encryptedPasswordBytes),
-			Name:              "root",
-			State:             model.UserStateEnabled,
-		}).Error; err != nil {
-			return err
-		}
-	}
-
 	return nil
 }
diff --git a/manager/handlers/cdn.go b/manager/handlers/cdn.go
index 2bb0111f..836cb53e 100644
--- a/manager/handlers/cdn.go
+++ b/manager/handlers/cdn.go
@@ -68,8 +68,7 @@ func (h *Handlers) DestroyCDN(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroyCDN(params.ID)
-	if err != nil {
+	if err := h.Service.DestroyCDN(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
diff --git a/manager/handlers/cdn_cluster.go b/manager/handlers/cdn_cluster.go
index 296561d5..b3f10f57 100644
--- a/manager/handlers/cdn_cluster.go
+++ b/manager/handlers/cdn_cluster.go
@@ -79,8 +79,7 @@ func (h *Handlers) DestroyCDNCluster(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroyCDNCluster(params.ID)
-	if err != nil {
+	if err := h.Service.DestroyCDNCluster(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
@@ -215,8 +214,7 @@ func (h *Handlers) AddCDNToCDNCluster(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.AddCDNToCDNCluster(params.ID, params.CDNID)
-	if err != nil {
+	if err := h.Service.AddCDNToCDNCluster(params.ID, params.CDNID); err != nil {
 		ctx.Error(err)
 		return
 	}
@@ -243,8 +241,7 @@ func (h *Handlers) AddSchedulerClusterToCDNCluster(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.AddSchedulerClusterToCDNCluster(params.ID, params.SchedulerClusterID)
-	if err != nil {
+	if err := h.Service.AddSchedulerClusterToCDNCluster(params.ID, params.SchedulerClusterID); err != nil {
 		ctx.Error(err)
 		return
 	}
diff --git a/manager/handlers/oauth.go b/manager/handlers/oauth.go
index 2cb40449..fc788900 100644
--- a/manager/handlers/oauth.go
+++ b/manager/handlers/oauth.go
@@ -1,11 +1,25 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package handlers
 
 import (
-	"fmt"
 	"net/http"
 
 	"d7y.io/dragonfly/v2/manager/types"
-	jwt "github.com/appleboy/gin-jwt/v2"
 	"github.com/gin-gonic/gin"
 )
 
@@ -16,10 +30,10 @@ import (
 // @Produce json
 // @Param Oauth body types.CreateOauthRequest true "Oauth"
 // @Success 200 {object} model.Oauth
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /oauths [post]
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /oauth [post]
 func (h *Handlers) CreateOauth(ctx *gin.Context) {
 	var json types.CreateOauthRequest
 	if err := ctx.ShouldBindJSON(&json); err != nil {
@@ -43,10 +57,10 @@ func (h *Handlers) CreateOauth(ctx *gin.Context) {
 // @Produce json
 // @Param id path string true "id"
 // @Success 200
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /oauths/{id} [delete]
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /oauth/{id} [delete]
 func (h *Handlers) DestroyOauth(ctx *gin.Context) {
 	var params types.OauthParams
 	if err := ctx.ShouldBindUri(&params); err != nil {
@@ -54,8 +68,7 @@ func (h *Handlers) DestroyOauth(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroyOauth(params.ID)
-	if err != nil {
+	if err := h.Service.DestroyOauth(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
@@ -63,72 +76,6 @@ func (h *Handlers) DestroyOauth(ctx *gin.Context) {
 	ctx.Status(http.StatusOK)
 }
 
-// @Summary start oauth signin
-// @Description start oauth signin
-// @Tags Oauth
-// @Accept json
-// @Produce json
-// @Param oauth_name path string true "oauth_name"
-// @Success 200
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /signin/{oauth_name} [get]
-func (h *Handlers) OauthSignin(ctx *gin.Context) {
-	var params types.OauthPathParams
-	if err := ctx.ShouldBindUri(&params); err != nil {
-		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
-		return
-	}
-
-	oauthURL, err := h.Service.OauthSignin(params.OauthName)
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-	fmt.Println(oauthURL)
-	ctx.Redirect(http.StatusMovedPermanently, oauthURL)
-}
-
-// @Summary start oauth callback
-// @Description start oauth callback
-// @Tags Oauth
-// @Param oauth_name path string true "oauth_name"
-// @Param code query string true "code"
-// @Success 200
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /callback/{oauth_name} [get]
-func (h *Handlers) OauthCallback(j *jwt.GinJWTMiddleware) func(*gin.Context) {
-	return func(ctx *gin.Context) {
-		var params types.OauthPathParams
-		if err := ctx.ShouldBindUri(&params); err != nil {
-			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
-			return
-		}
-
-		code := ctx.Query("code")
-		if code == "" {
-			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": "code is required"})
-			return
-		}
-
-		user, err := h.Service.OauthCallback(params.OauthName, code)
-		if err != nil {
-			ctx.Error(err)
-			return
-		}
-		jwtToken, _, err := j.TokenGenerator(user)
-		if err != nil {
-			ctx.Error(err)
-			return
-		}
-		ctx.SetCookie("jwt", jwtToken, 3600, "", "", false, true)
-		ctx.Redirect(http.StatusMovedPermanently, "/")
-	}
-}
-
 // @Summary Update Oauth
 // @Description Update by json config
 // @Tags Oauth
@@ -137,9 +84,9 @@ func (h *Handlers) OauthCallback(j *jwt.GinJWTMiddleware) func(*gin.Context) {
 // @Param id path string true "id"
 // @Param Oauth body types.UpdateOauthRequest true "Oauth"
 // @Success 200 {object} model.Oauth
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
+// @Failure 400
+// @Failure 404
+// @Failure 500
 // @Router /oauth/{id} [patch]
 func (h *Handlers) UpdateOauth(ctx *gin.Context) {
 	var params types.OauthParams
@@ -170,9 +117,9 @@ func (h *Handlers) UpdateOauth(ctx *gin.Context) {
 // @Produce json
 // @Param id path string true "id"
 // @Success 200 {object} model.Oauth
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
+// @Failure 400
+// @Failure 404
+// @Failure 500
 // @Router /oauth/{id} [get]
 func (h *Handlers) GetOauth(ctx *gin.Context) {
 	var params types.OauthParams
@@ -195,17 +142,33 @@ func (h *Handlers) GetOauth(ctx *gin.Context) {
 // @Tags Oauth
 // @Accept json
 // @Produce json
+// @Param page query int true "current page" default(0)
+// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
 // @Success 200 {object} []model.Oauth
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
+// @Failure 400
+// @Failure 404
+// @Failure 500
 // @Router /oauth [get]
 func (h *Handlers) GetOauths(ctx *gin.Context) {
-	oauths, err := h.Service.GetOauths()
+	var query types.GetOauthsQuery
+	if err := ctx.ShouldBindQuery(&query); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	h.setPaginationDefault(&query.Page, &query.PerPage)
+	oauth, err := h.Service.GetOauths(query)
+	if err != nil {
+		ctx.Error(err)
+		return
+	}
+
+	totalCount, err := h.Service.OauthTotalCount(query)
 	if err != nil {
 		ctx.Error(err)
 		return
 	}
 
-	ctx.JSON(http.StatusOK, oauths)
+	h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(totalCount))
+	ctx.JSON(http.StatusOK, oauth)
 }
diff --git a/manager/handlers/scheduler.go b/manager/handlers/scheduler.go
index 0e48a13f..e89a5230 100644
--- a/manager/handlers/scheduler.go
+++ b/manager/handlers/scheduler.go
@@ -68,8 +68,7 @@ func (h *Handlers) DestroyScheduler(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroyScheduler(params.ID)
-	if err != nil {
+	if err := h.Service.DestroyScheduler(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
diff --git a/manager/handlers/scheduler_cluster.go b/manager/handlers/scheduler_cluster.go
index ec13e9fd..78d413e2 100644
--- a/manager/handlers/scheduler_cluster.go
+++ b/manager/handlers/scheduler_cluster.go
@@ -79,8 +79,7 @@ func (h *Handlers) DestroySchedulerCluster(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroySchedulerCluster(params.ID)
-	if err != nil {
+	if err := h.Service.DestroySchedulerCluster(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
diff --git a/manager/handlers/security_group.go b/manager/handlers/security_group.go
index f4f5c83e..70886e24 100644
--- a/manager/handlers/security_group.go
+++ b/manager/handlers/security_group.go
@@ -68,8 +68,7 @@ func (h *Handlers) DestroySecurityGroup(ctx *gin.Context) {
 		return
 	}
 
-	err := h.Service.DestroySecurityGroup(params.ID)
-	if err != nil {
+	if err := h.Service.DestroySecurityGroup(params.ID); err != nil {
 		ctx.Error(err)
 		return
 	}
diff --git a/manager/handlers/settings.go b/manager/handlers/settings.go
deleted file mode 100644
index 3db7f10c..00000000
--- a/manager/handlers/settings.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package handlers
-
-import (
-	"net/http"
-
-	"d7y.io/dragonfly/v2/manager/types"
-	"github.com/gin-gonic/gin"
-)
-
-// @Summary Create Setting
-// @Description create by json config
-// @Tags Setting
-// @Accept json
-// @Produce json
-// @Param Setting body types.CreateSettingRequest true "Setting"
-// @Success 200 {object} model.Setting
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /settings [post]
-func (h *Handlers) CreateSetting(ctx *gin.Context) {
-	var json types.CreateSettingRequest
-	if err := ctx.ShouldBindJSON(&json); err != nil {
-		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
-		return
-	}
-
-	setting, err := h.Service.CreateSetting(json)
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	ctx.JSON(http.StatusOK, setting)
-}
-
-// @Summary Destroy Setting
-// @Description Destroy by key
-// @Tags Setting
-// @Accept json
-// @Produce json
-// @Param id path string true "key"
-// @Success 200
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /settings/{id} [delete]
-func (h *Handlers) DestroySetting(ctx *gin.Context) {
-	var params types.SettingParams
-	if err := ctx.ShouldBindUri(&params); err != nil {
-		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
-		return
-	}
-
-	err := h.Service.DestroySetting(params.Key)
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	ctx.Status(http.StatusOK)
-}
-
-// @Summary Update Setting
-// @Description Update by json config
-// @Tags Setting
-// @Accept json
-// @Produce json
-// @Param key path string true "key"
-// @Param Setting body types.UpdateSettingRequest true "Setting"
-// @Success 200 {object} model.Setting
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /settings/{id} [patch]
-func (h *Handlers) UpdateSetting(ctx *gin.Context) {
-
-	var params types.SettingParams
-	if err := ctx.ShouldBindUri(&params); err != nil {
-		ctx.Error(err)
-		return
-	}
-	var json types.UpdateSettingRequest
-	if err := ctx.ShouldBindJSON(&json); err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	setting, err := h.Service.UpdateSetting(params.Key, json)
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	ctx.JSON(http.StatusOK, setting)
-}
-
-// @Summary Get Settings
-// @Description Get Settings
-// @Tags Setting
-// @Accept json
-// @Produce json
-// @Param page query int true "current page" default(0)
-// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
-// @Success 200 {object} []model.Setting
-// @Failure 400 {object} HTTPError
-// @Failure 404 {object} HTTPError
-// @Failure 500 {object} HTTPError
-// @Router /settings [get]
-func (h *Handlers) GetSettings(ctx *gin.Context) {
-	var query types.GetSettingsQuery
-	if err := ctx.ShouldBindQuery(&query); err != nil {
-		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
-		return
-	}
-
-	h.setPaginationDefault(&query.Page, &query.PerPage)
-	settings, err := h.Service.GetSettings(query)
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	totalCount, err := h.Service.SettingTotalCount()
-	if err != nil {
-		ctx.Error(err)
-		return
-	}
-
-	h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(totalCount))
-	ctx.JSON(http.StatusOK, settings)
-}
diff --git a/manager/handlers/user.go b/manager/handlers/user.go
index fce32dfc..9122ed6c 100644
--- a/manager/handlers/user.go
+++ b/manager/handlers/user.go
@@ -20,6 +20,7 @@ import (
 	"net/http"
 
 	"d7y.io/dragonfly/v2/manager/types"
+	jwt "github.com/appleboy/gin-jwt/v2"
 	"github.com/gin-gonic/gin"
 )
 
@@ -80,6 +81,68 @@ func (h *Handlers) ResetPassword(ctx *gin.Context) {
 	ctx.Status(http.StatusOK)
 }
 
+// @Summary Oauth Signin
+// @Description oauth signin by json config
+// @Tags User
+// @Accept json
+// @Produce json
+// @Param name path string true "name"
+// @Success 200
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /user/signin/{name} [get]
+func (h *Handlers) OauthSignin(ctx *gin.Context) {
+	var params types.OauthSigninParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	authURL, err := h.Service.OauthSignin(params.Name)
+	if err != nil {
+		ctx.Error(err)
+		return
+	}
+
+	ctx.Redirect(http.StatusFound, authURL)
+}
+
+// @Summary Oauth Signin Callback
+// @Description oauth signin callback by json config
+// @Tags Oauth
+// @Param name path string true "name"
+// @Param code query string true "code"
+// @Success 200
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /user/signin/{name}/callback [get]
+func (h *Handlers) OauthSigninCallback(j *jwt.GinJWTMiddleware) func(*gin.Context) {
+	return func(ctx *gin.Context) {
+		var params types.OauthSigninCallbackParams
+		if err := ctx.ShouldBindUri(&params); err != nil {
+			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+			return
+		}
+
+		var query types.OauthSigninCallbackQuery
+		if err := ctx.ShouldBindQuery(&query); err != nil {
+			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+			return
+		}
+
+		user, err := h.Service.OauthSigninCallback(params.Name, query.Code)
+		if err != nil {
+			ctx.Error(err)
+			return
+		}
+
+		ctx.Set("user", user)
+		j.LoginHandler(ctx)
+	}
+}
+
 // @Summary Get User Roles
 // @Description get roles by json config
 // @Tags User
diff --git a/manager/manager.go b/manager/manager.go
index 62d32115..709680c7 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -91,16 +91,16 @@ func New(cfg *config.Config) (*Server, error) {
 	if err != nil {
 		return nil, err
 	}
+	restServer := &http.Server{
+		Addr:    cfg.Server.REST.Addr,
+		Handler: router,
+	}
 
 	// Initialize roles and check roles
-	err = rbac.InitRBAC(enforcer, router)
+	err = rbac.InitRBAC(enforcer, router, db.DB)
 	if err != nil {
 		return nil, err
 	}
-	restServer := &http.Server{
-		Addr:    cfg.Server.REST.Addr,
-		Handler: router,
-	}
 
 	// Initialize GRPC server
 	grpcService := service.NewGRPC(db, cache, searcher)
diff --git a/manager/middlewares/error.go b/manager/middlewares/error.go
index b3cde80a..92afbe1a 100644
--- a/manager/middlewares/error.go
+++ b/manager/middlewares/error.go
@@ -25,6 +25,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/go-sql-driver/mysql"
 	"github.com/pkg/errors"
+	"golang.org/x/crypto/bcrypt"
 	"gorm.io/gorm"
 )
 
@@ -49,20 +50,32 @@ func Error() gin.HandlerFunc {
 				c.JSON(http.StatusBadRequest, ErrorResponse{
 					Message: http.StatusText(http.StatusBadRequest),
 				})
+				c.Abort()
 				return
 			default:
 				c.JSON(http.StatusInternalServerError, ErrorResponse{
 					Message: http.StatusText(http.StatusInternalServerError),
 				})
+				c.Abort()
 				return
 			}
 		}
 
+		// Bcrypt error handler
+		if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
+			c.JSON(http.StatusUnauthorized, ErrorResponse{
+				Message: http.StatusText(http.StatusUnauthorized),
+			})
+			c.Abort()
+			return
+		}
+
 		// GORM error handler
 		if errors.Is(err.Err, gorm.ErrRecordNotFound) {
 			c.JSON(http.StatusNotFound, ErrorResponse{
 				Message: http.StatusText(http.StatusNotFound),
 			})
+			c.Abort()
 			return
 		}
 
@@ -73,11 +86,13 @@ func Error() gin.HandlerFunc {
 				c.JSON(http.StatusConflict, ErrorResponse{
 					Message: http.StatusText(http.StatusConflict),
 				})
+				c.Abort()
 				return
 			default:
 				c.JSON(http.StatusInternalServerError, ErrorResponse{
 					Message: http.StatusText(http.StatusInternalServerError),
 				})
+				c.Abort()
 				return
 			}
 		}
diff --git a/manager/middlewares/jwt.go b/manager/middlewares/jwt.go
index 8302cf3c..e7253d65 100644
--- a/manager/middlewares/jwt.go
+++ b/manager/middlewares/jwt.go
@@ -38,8 +38,8 @@ func Jwt(service service.REST) (*jwt.GinJWTMiddleware, error) {
 	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
 		Realm:       "Dragonfly",
 		Key:         []byte("Secret Key"),
-		Timeout:     time.Hour,
-		MaxRefresh:  time.Hour,
+		Timeout:     2 * 24 * time.Hour,
+		MaxRefresh:  2 * 24 * time.Hour,
 		IdentityKey: identityKey,
 
 		IdentityHandler: func(c *gin.Context) interface{} {
@@ -63,35 +63,45 @@ func Jwt(service service.REST) (*jwt.GinJWTMiddleware, error) {
 				return nil
 			}
 
-			u := &user{
+			user := &user{
 				name: name.(string),
 				id:   id.(uint),
 			}
 
-			c.Set("name", u.name)
-			c.Set("id", u.id)
-			return u
+			c.Set("name", user.name)
+			c.Set("id", user.id)
+			return user
 		},
 
 		Authenticator: func(c *gin.Context) (interface{}, error) {
+			// Oauth2 signin
+			if rawUser, ok := c.Get("user"); ok {
+				user, ok := rawUser.(*model.User)
+				if !ok {
+					return "", jwt.ErrFailedAuthentication
+				}
+				return user, nil
+			}
+
+			// Normal signin
 			var json types.SignInRequest
 			if err := c.ShouldBindJSON(&json); err != nil {
 				return "", jwt.ErrMissingLoginValues
 			}
 
-			u, err := service.SignIn(json)
+			user, err := service.SignIn(json)
 			if err != nil {
 				return "", jwt.ErrFailedAuthentication
 			}
 
-			return u, nil
+			return user, nil
 		},
 
 		PayloadFunc: func(data interface{}) jwt.MapClaims {
-			if u, ok := data.(*model.User); ok {
+			if user, ok := data.(*model.User); ok {
 				return jwt.MapClaims{
-					identityKey: u.ID,
-					"name":      u.Name,
+					identityKey: user.ID,
+					"name":      user.Name,
 				}
 			}
 
@@ -105,6 +115,13 @@ func Jwt(service service.REST) (*jwt.GinJWTMiddleware, error) {
 		},
 
 		LoginResponse: func(c *gin.Context, code int, token string, expire time.Time) {
+			// Oauth2 signin
+			if _, ok := c.Get("user"); ok {
+				c.Redirect(http.StatusFound, "/")
+				return
+			}
+
+			// Normal signin
 			c.JSON(code, gin.H{
 				"token":  token,
 				"expire": expire.Format(time.RFC3339),
diff --git a/manager/model/models.go b/manager/model/models.go
index 421a5590..98d242cf 100644
--- a/manager/model/models.go
+++ b/manager/model/models.go
@@ -32,7 +32,7 @@ type Model struct {
 	ID        uint                  `gorm:"primarykey;comment:id" json:"id"`
 	CreatedAt time.Time             `gorm:"column:created_at;type:timestamp;comment:created timestamp" json:"created_at"`
 	UpdatedAt time.Time             `gorm:"column:updated_at;type:timestamp;comment:updated timestamp" json:"updated_at"`
-	IsDel     soft_delete.DeletedAt `gorm:"softDelete:flag;comment:soft delete flag"`
+	IsDel     soft_delete.DeletedAt `gorm:"softDelete:flag;comment:soft delete flag" json:"-"`
 }
 
 func Paginate(page, perPage int) func(db *gorm.DB) *gorm.DB {
diff --git a/manager/model/oauth.go b/manager/model/oauth.go
index 06c7d5b4..f9a24054 100644
--- a/manager/model/oauth.go
+++ b/manager/model/oauth.go
@@ -2,12 +2,9 @@ package model
 
 type Oauth struct {
 	Model
-	Name         string `gorm:"column:name;type:varchar(256);index:uk_oauth_name,unique;not null;comment:oauth name" json:"name"`
-	ClientID     string `gorm:"column:client_id;type:varchar(256);index:uk_oauth_client_id,unique;not null;comment:client id for oauth" json:"client_id"`
-	ClientSecret string `gorm:"column:client_secret;type:varchar(1024);comment:client secret for oauth" json:"client_secret"`
-	// scope list split by ','
-	Scopes      string `gorm:"column:scopes;type:varchar(1024);comment:scopes" json:"scopes"`
-	UserInfoURL string `gorm:"column:user_info_url;type:varchar(256);not null;comment:user info url" json:"user_info_url"`
-	AuthURL     string `gorm:"column:auth_url;type:varchar(256);not null;comment:auth url" json:"auth_url"`
-	TokenURL    string `gorm:"column:token_url;type:varchar(256);not null;comment:token url" json:"token_url"`
+	Name         string `gorm:"column:name;type:varchar(256);index:uk_oauth2_name,unique;not null;comment:oauth2 name" json:"name"`
+	BIO          string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"`
+	ClientID     string `gorm:"column:client_id;type:varchar(256);index:uk_oauth2_client_id,unique;not null;comment:client id for oauth2" json:"client_id"`
+	ClientSecret string `gorm:"column:client_secret;type:varchar(1024);not null;comment:client secret for oauth2" json:"client_secret"`
+	RedirectURL  string `gorm:"column:redirect_url;type:varchar(1024);comment:authorization callback url" json:"redirect_url"`
 }
diff --git a/manager/model/settings.go b/manager/model/settings.go
deleted file mode 100644
index a9048316..00000000
--- a/manager/model/settings.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package model
-
-type Settings struct {
-	Model
-	Key   string `gorm:"column:key;type:varchar(256);index:uk_settings_key,unique;not null;comment:setting key" json:"key"`
-	Value string `gorm:"column:value;type:varchar(256);not null;comment:setting value" json:"value"`
-}
diff --git a/manager/model/user.go b/manager/model/user.go
index b29d5401..3697cc74 100644
--- a/manager/model/user.go
+++ b/manager/model/user.go
@@ -25,7 +25,7 @@ type User struct {
 	Model
 	Email             string `gorm:"column:email;type:varchar(256);index:uk_user_email,unique;not null;comment:email address" json:"email"`
 	Name              string `gorm:"column:name;type:varchar(256);index:uk_user_name,unique;not null;comment:name" json:"name"`
-	EncryptedPassword string `gorm:"column:encrypted_password;not null;size:1024;comment:encrypted password" json:"-"`
+	EncryptedPassword string `gorm:"column:encrypted_password;size:1024;comment:encrypted password" json:"-"`
 	Avatar            string `gorm:"column:avatar;type:varchar(256);comment:avatar address" json:"avatar"`
 	Phone             string `gorm:"column:phone;type:varchar(256);comment:phone number" json:"phone"`
 	PrivateToken      string `gorm:"column:private_token;type:varchar(256);comment:private token" json:"private_token"`
diff --git a/manager/permission/rbac/rbac.go b/manager/permission/rbac/rbac.go
index 7534a923..76389fdc 100644
--- a/manager/permission/rbac/rbac.go
+++ b/manager/permission/rbac/rbac.go
@@ -18,6 +18,7 @@ package rbac
 
 import (
 	"errors"
+	"fmt"
 	"net/http"
 	"regexp"
 
@@ -27,6 +28,7 @@ import (
 	"github.com/casbin/casbin/v2/model"
 	gormadapter "github.com/casbin/gorm-adapter/v3"
 	"github.com/gin-gonic/gin"
+	"golang.org/x/crypto/bcrypt"
 	"gorm.io/gorm"
 )
 
@@ -48,6 +50,16 @@ e = some(where (p.eft == allow))
 m = g(r.sub, p.sub) && r.obj == p.obj && (r.act == p.act || p.act == "*")
 `
 
+const (
+	RootRole  = "root"
+	GuestRole = "guest"
+)
+
+const (
+	AllAction  = "*"
+	ReadAction = "read"
+)
+
 func NewEnforcer(gdb *gorm.DB) (*casbin.Enforcer, error) {
 	adapter, err := gormadapter.NewAdapterByDBWithCustomTable(gdb, &managermodel.CasbinRule{})
 	if err != nil {
@@ -67,29 +79,57 @@ func NewEnforcer(gdb *gorm.DB) (*casbin.Enforcer, error) {
 	return enforcer, nil
 }
 
-func InitRBAC(e *casbin.Enforcer, g *gin.Engine) error {
+func InitRBAC(e *casbin.Enforcer, g *gin.Engine, db *gorm.DB) error {
+	// Create roles
 	permissions := GetPermissions(g)
 	for _, permission := range permissions {
-		if _, err := e.AddPermissionForUser("root", permission.Object, "*"); err != nil {
+		if _, err := e.AddPermissionForUser(RootRole, permission.Object, AllAction); err != nil {
+			return err
+		}
+
+		if _, err := e.AddPermissionForUser(GuestRole, permission.Object, ReadAction); err != nil {
 			return err
 		}
 	}
 
-	if _, err := e.AddRoleForUser("1", "root"); err != nil {
+	// Create root user for the first time
+	var rootUserCount int64
+	if err := db.Model(managermodel.User{}).Count(&rootUserCount).Error; err != nil {
 		return err
 	}
 
+	if rootUserCount <= 0 {
+		encryptedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte("dragonfly"), bcrypt.MinCost)
+		if err != nil {
+			return err
+		}
+
+		rootUser := managermodel.User{
+			EncryptedPassword: string(encryptedPasswordBytes),
+			Name:              "root",
+			State:             managermodel.UserStateEnabled,
+		}
+
+		if err := db.Create(&rootUser).Error; err != nil {
+			return err
+		}
+
+		if _, err := e.AddRoleForUser(fmt.Sprint(rootUser.ID), RootRole); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
 type Permission struct {
-	Object string
-	Action string
+	Object string `json:"object" binding:"required"`
+	Action string `json:"action" binding:"required,oneof=read *"`
 }
 
 func GetPermissions(g *gin.Engine) []Permission {
 	permissions := []Permission{}
-	actions := []string{"read", "*"}
+	actions := []string{AllAction, ReadAction}
 	for _, permission := range GetAPIGroupNames(g) {
 		for _, action := range actions {
 			permissions = append(permissions, Permission{
@@ -129,9 +169,9 @@ func GetAPIGroupName(path string) (string, error) {
 }
 
 func HTTPMethodToAction(method string) string {
-	action := "read"
+	action := ReadAction
 	if method == http.MethodDelete || method == http.MethodPatch || method == http.MethodPut || method == http.MethodPost {
-		action = "*"
+		action = AllAction
 	}
 
 	return action
diff --git a/manager/router/router.go b/manager/router/router.go
index 245bc54d..0b0e7fc8 100644
--- a/manager/router/router.go
+++ b/manager/router/router.go
@@ -83,6 +83,8 @@ func Init(console bool, verbose bool, publicPath string, service service.REST, e
 	u.POST("/signin", jwt.LoginHandler)
 	u.POST("/signout", jwt.LogoutHandler)
 	u.POST("/signup", h.SignUp)
+	u.GET("/signin/:name", h.OauthSignin)
+	u.GET("/signin/:name/callback", h.OauthSigninCallback(jwt))
 	u.POST("/refresh_token", jwt.RefreshHandler)
 	u.POST("/:id/reset_password", h.ResetPassword)
 	u.GET("/:id/roles", h.GetRolesForUser)
@@ -104,13 +106,11 @@ func Init(console bool, verbose bool, publicPath string, service service.REST, e
 
 	// Oauth
 	oa := apiv1.Group("/oauth")
-	oa.GET("", h.GetOauths)
-	oa.GET("/:id", h.GetOauth)
-	oa.DELETE("/:id", h.DestroyOauth)
-	oa.PUT("/:id", h.UpdateOauth)
 	oa.POST("", h.CreateOauth)
-	oa.GET("/signin/:oauth_name", h.OauthSignin)
-	oa.GET("/callback/:oauth_name", h.OauthCallback(jwt))
+	oa.DELETE(":id", h.DestroyOauth)
+	oa.PATCH(":id", h.UpdateOauth)
+	oa.GET(":id", h.GetOauth)
+	oa.GET("", h.GetOauths)
 
 	// Scheduler Cluster
 	sc := apiv1.Group("/scheduler-clusters")
@@ -129,13 +129,6 @@ func Init(console bool, verbose bool, publicPath string, service service.REST, e
 	s.GET(":id", h.GetScheduler)
 	s.GET("", h.GetSchedulers)
 
-	// Settings
-	st := apiv1.Group("/settings")
-	st.POST("", h.CreateSetting)
-	st.DELETE(":id", h.DestroySetting)
-	st.PATCH("", h.UpdateSetting)
-	st.GET("", h.GetSettings)
-
 	// CDN Cluster
 	cc := apiv1.Group("/cdn-clusters")
 	cc.POST("", h.CreateCDNCluster)
diff --git a/manager/service/oauth.go b/manager/service/oauth.go
index c8eeb92d..6bd7befd 100644
--- a/manager/service/oauth.go
+++ b/manager/service/oauth.go
@@ -1,47 +1,40 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package service
 
 import (
-	"d7y.io/dragonfly/v2/manager/auth/oauth"
 	"d7y.io/dragonfly/v2/manager/model"
 	"d7y.io/dragonfly/v2/manager/types"
-	"golang.org/x/oauth2/github"
-	"golang.org/x/oauth2/google"
 )
 
 func (s *rest) CreateOauth(json types.CreateOauthRequest) (*model.Oauth, error) {
-	o := model.Oauth{}
-	o.ClientID = json.ClientID
-	o.ClientSecret = json.ClientSecret
-	o.Name = json.Name
-	switch json.Name {
-	case oauth.Google:
-		o.AuthURL = google.Endpoint.AuthURL
-		o.TokenURL = google.Endpoint.TokenURL
-		o.Scopes = oauth.GoogleScopes
-		o.UserInfoURL = oauth.GoogleUserInfoURL
-
-	case oauth.Github:
-		o.AuthURL = github.Endpoint.AuthURL
-		o.TokenURL = github.Endpoint.TokenURL
-		o.Scopes = oauth.GithubScopes
-		o.UserInfoURL = oauth.GithubUserInfoURL
-	default:
-		o = model.Oauth{
-			ClientID:     json.ClientID,
-			ClientSecret: json.ClientSecret,
-			Name:         json.Name,
-			Scopes:       json.Scopes,
-			AuthURL:      json.AuthURL,
-			TokenURL:     json.TokenURL,
-			UserInfoURL:  json.UserInfoURL,
-		}
+	oauth := model.Oauth{
+		Name:         json.Name,
+		BIO:          json.BIO,
+		ClientID:     json.ClientID,
+		ClientSecret: json.ClientSecret,
+		RedirectURL:  json.RedirectURL,
 	}
 
-	if err := s.db.Create(&o).Error; err != nil {
+	if err := s.db.Create(&oauth).Error; err != nil {
 		return nil, err
 	}
 
-	return &o, nil
+	return &oauth, nil
 }
 
 func (s *rest) DestroyOauth(id uint) error {
@@ -55,10 +48,11 @@ func (s *rest) DestroyOauth(id uint) error {
 func (s *rest) UpdateOauth(id uint, json types.UpdateOauthRequest) (*model.Oauth, error) {
 	oauth := model.Oauth{}
 	if err := s.db.First(&oauth, id).Updates(model.Oauth{
+		Name:         json.Name,
+		BIO:          json.BIO,
 		ClientID:     json.ClientID,
 		ClientSecret: json.ClientSecret,
-		AuthURL:      json.AuthURL,
-		TokenURL:     json.TokenURL,
+		RedirectURL:  json.RedirectURL,
 	}).Error; err != nil {
 		return nil, err
 	}
@@ -75,48 +69,26 @@ func (s *rest) GetOauth(id uint) (*model.Oauth, error) {
 	return &oauth, nil
 }
 
-func (s *rest) GetOauths() (*[]model.Oauth, error) {
+func (s *rest) GetOauths(q types.GetOauthsQuery) (*[]model.Oauth, error) {
 	oauths := []model.Oauth{}
-	if err := s.db.Find(&oauths).Error; err != nil {
+	if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Oauth{
+		Name:     q.Name,
+		ClientID: q.ClientID,
+	}).Find(&oauths).Error; err != nil {
 		return nil, err
 	}
-	return &oauths, nil
-}
 
-func (s *rest) OauthSignin(name string) (string, error) {
-	oauthModel := model.Oauth{}
-	if err := s.db.First(&oauthModel, model.Oauth{Name: name}).Error; err != nil {
-		return "", err
-	}
-
-	o, err := oauth.New(&oauthModel, s.db)
-	if err != nil {
-		return "", err
-	}
-	return o.AuthCodeURL(), nil
+	return &oauths, nil
 }
 
-func (s *rest) OauthCallback(name, code string) (*model.User, error) {
-	oauthModel := model.Oauth{}
-	if err := s.db.First(&oauthModel, model.Oauth{Name: name}).Error; err != nil {
-		return nil, err
-	}
-	o, err := oauth.New(&oauthModel, s.db)
-	if err != nil {
-		return nil, err
+func (s *rest) OauthTotalCount(q types.GetOauthsQuery) (int64, error) {
+	var count int64
+	if err := s.db.Model(&model.Oauth{}).Where(&model.Oauth{
+		Name:     q.Name,
+		ClientID: q.ClientID,
+	}).Count(&count).Error; err != nil {
+		return 0, err
 	}
 
-	token, err := o.ExchangeTokenByCode(code)
-	if err != nil {
-		return nil, err
-	}
-	u, err := o.GetOauthUserInfo(token)
-	if err != nil {
-		return nil, err
-	}
-	user, err := o.OauthLinkUser(u, s.db)
-	if err != nil {
-		return nil, err
-	}
-	return user, nil
+	return count, nil
 }
diff --git a/manager/service/role.go b/manager/service/role.go
index e6d799ac..f7141553 100644
--- a/manager/service/role.go
+++ b/manager/service/role.go
@@ -40,7 +40,7 @@ func (s *rest) GetRole(role string) [][]string {
 }
 
 func (s *rest) GetRoles() []string {
-	return s.enforcer.GetAllRoles()
+	return s.enforcer.GetAllSubjects()
 }
 
 func (s *rest) AddPermissionForRole(role string, json types.AddPermissionForRoleRequest) (bool, error) {
diff --git a/manager/service/service.go b/manager/service/service.go
index 023f5b65..70a4ac94 100644
--- a/manager/service/service.go
+++ b/manager/service/service.go
@@ -33,6 +33,8 @@ import (
 type REST interface {
 	SignIn(types.SignInRequest) (*model.User, error)
 	SignUp(types.SignUpRequest) (*model.User, error)
+	OauthSignin(string) (string, error)
+	OauthSigninCallback(string, string) (*model.User, error)
 	ResetPassword(uint, types.ResetPasswordRequest) error
 	GetRolesForUser(uint) ([]string, error)
 	AddRoleForUser(types.AddRoleForUserParams) (bool, error)
@@ -47,6 +49,13 @@ type REST interface {
 
 	GetPermissions(*gin.Engine) []rbac.Permission
 
+	CreateOauth(types.CreateOauthRequest) (*model.Oauth, error)
+	DestroyOauth(uint) error
+	UpdateOauth(uint, types.UpdateOauthRequest) (*model.Oauth, error)
+	GetOauth(uint) (*model.Oauth, error)
+	GetOauths(types.GetOauthsQuery) (*[]model.Oauth, error)
+	OauthTotalCount(types.GetOauthsQuery) (int64, error)
+
 	CreateCDNCluster(types.CreateCDNClusterRequest) (*model.CDNCluster, error)
 	CreateCDNClusterWithSecurityGroupDomain(types.CreateCDNClusterRequest) (*model.CDNCluster, error)
 	DestroyCDNCluster(uint) error
@@ -82,12 +91,6 @@ type REST interface {
 	GetSchedulers(types.GetSchedulersQuery) (*[]model.Scheduler, error)
 	SchedulerTotalCount(types.GetSchedulersQuery) (int64, error)
 
-	CreateSetting(types.CreateSettingRequest) (*model.Settings, error)
-	DestroySetting(string) error
-	UpdateSetting(string, types.UpdateSettingRequest) (*model.Settings, error)
-	GetSettings(types.GetSettingsQuery) (*[]model.Settings, error)
-	SettingTotalCount() (int64, error)
-
 	CreateSecurityGroup(types.CreateSecurityGroupRequest) (*model.SecurityGroup, error)
 	DestroySecurityGroup(uint) error
 	UpdateSecurityGroup(uint, types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error)
@@ -99,14 +102,6 @@ type REST interface {
 
 	CreatePreheat(types.CreatePreheatRequest) (*types.Preheat, error)
 	GetPreheat(string) (*types.Preheat, error)
-
-	CreateOauth(types.CreateOauthRequest) (*model.Oauth, error)
-	DestroyOauth(uint) error
-	UpdateOauth(uint, types.UpdateOauthRequest) (*model.Oauth, error)
-	GetOauth(uint) (*model.Oauth, error)
-	GetOauths() (*[]model.Oauth, error)
-	OauthSignin(name string) (string, error)
-	OauthCallback(name, code string) (*model.User, error)
 }
 
 type rest struct {
diff --git a/manager/service/settings.go b/manager/service/settings.go
deleted file mode 100644
index 2ecc28a0..00000000
--- a/manager/service/settings.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package service
-
-import (
-	"errors"
-
-	"d7y.io/dragonfly/v2/manager/model"
-	"d7y.io/dragonfly/v2/manager/types"
-	"d7y.io/dragonfly/v2/pkg/util/stringutils"
-)
-
-var AvaliableSettings = []string{
-	"server_domain",
-}
-
-func (s *rest) CreateSetting(json types.CreateSettingRequest) (*model.Settings, error) {
-	if !stringutils.Contains(AvaliableSettings, json.Key) {
-		return nil, errors.New("invalid setting key")
-	}
-	setting := model.Settings{
-		Key:   json.Key,
-		Value: json.Value,
-	}
-
-	if err := s.db.Create(&setting).Error; err != nil {
-		return nil, err
-	}
-
-	return &setting, nil
-}
-
-func (s *rest) DestroySetting(key string) error {
-	if err := s.db.Unscoped().Delete(&model.Settings{}, model.Settings{Key: key}).Error; err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (s *rest) UpdateSetting(key string, json types.UpdateSettingRequest) (*model.Settings, error) {
-	setting := model.Settings{}
-	if err := s.db.First(&setting, model.Settings{Key: key}).Updates(model.Settings{
-		Key:   json.Key,
-		Value: json.Value,
-	}).Error; err != nil {
-		return nil, err
-	}
-
-	return &setting, nil
-}
-
-func (s *rest) GetSettings(q types.GetSettingsQuery) (*[]model.Settings, error) {
-	settings := []model.Settings{}
-	if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Find(&settings).Error; err != nil {
-		return nil, err
-	}
-
-	return &settings, nil
-}
-
-func (s *rest) SettingTotalCount() (int64, error) {
-	var count int64
-	if err := s.db.Model(&model.Settings{}).Count(&count).Error; err != nil {
-		return 0, err
-	}
-
-	return count, nil
-}
diff --git a/manager/service/user.go b/manager/service/user.go
index 910d37b2..ad884402 100644
--- a/manager/service/user.go
+++ b/manager/service/user.go
@@ -19,8 +19,14 @@ package service
 import (
 	"fmt"
 
+	"github.com/pkg/errors"
+
+	manageroauth "d7y.io/dragonfly/v2/manager/auth/oauth"
 	"d7y.io/dragonfly/v2/manager/model"
+	"d7y.io/dragonfly/v2/manager/permission/rbac"
 	"d7y.io/dragonfly/v2/manager/types"
+	"github.com/VividCortex/mysqlerr"
+	"github.com/go-sql-driver/mysql"
 	"golang.org/x/crypto/bcrypt"
 )
 
@@ -32,8 +38,7 @@ func (s *rest) SignIn(json types.SignInRequest) (*model.User, error) {
 		return nil, err
 	}
 
-	err := bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte(json.Password))
-	if err != nil {
+	if err := bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte(json.Password)); err != nil {
 		return nil, err
 	}
 
@@ -85,6 +90,66 @@ func (s *rest) SignUp(json types.SignUpRequest) (*model.User, error) {
 		return nil, err
 	}
 
+	if _, err := s.enforcer.AddRoleForUser(fmt.Sprint(user.ID), rbac.GuestRole); err != nil {
+		return nil, err
+	}
+
+	return &user, nil
+}
+
+func (s *rest) OauthSignin(name string) (string, error) {
+	oauth := model.Oauth{}
+	if err := s.db.First(&oauth, model.Oauth{Name: name}).Error; err != nil {
+		return "", err
+	}
+
+	o, err := manageroauth.New(oauth.Name, oauth.ClientID, oauth.ClientSecret, oauth.RedirectURL)
+	if err != nil {
+		return "", err
+	}
+
+	return o.AuthCodeURL(), nil
+}
+
+func (s *rest) OauthSigninCallback(name, code string) (*model.User, error) {
+	oauth := model.Oauth{}
+	if err := s.db.First(&oauth, model.Oauth{Name: name}).Error; err != nil {
+		return nil, err
+	}
+
+	o, err := manageroauth.New(oauth.Name, oauth.ClientID, oauth.ClientSecret, oauth.RedirectURL)
+	if err != nil {
+		return nil, err
+	}
+
+	token, err := o.Exchange(code)
+	if err != nil {
+		return nil, err
+	}
+
+	oauthUser, err := o.GetUser(token)
+	if err != nil {
+		return nil, err
+	}
+
+	user := model.User{
+		Name:   oauthUser.Name,
+		Email:  oauthUser.Email,
+		Avatar: oauthUser.Avatar,
+		State:  model.UserStateEnabled,
+	}
+	if err := s.db.Create(&user).Error; err != nil {
+		if err, ok := errors.Cause(err).(*mysql.MySQLError); ok && err.Number == mysqlerr.ER_DUP_ENTRY {
+			return &user, nil
+		}
+
+		return nil, err
+	}
+
+	if _, err := s.enforcer.AddRoleForUser(fmt.Sprint(user.ID), rbac.GuestRole); err != nil {
+		return nil, err
+	}
+
 	return &user, nil
 }
 
diff --git a/manager/types/cdn.go b/manager/types/cdn.go
index 6504d8f3..b7d87d3c 100644
--- a/manager/types/cdn.go
+++ b/manager/types/cdn.go
@@ -46,7 +46,7 @@ type GetCDNsQuery struct {
 	IP           string `form:"ip" binding:"omitempty"`
 	Port         int32  `form:"port" binding:"omitempty"`
 	DownloadPort int32  `form:"download_port" binding:"omitempty"`
-	CDNClusterID uint   `json:"cdn_cluster_id" binding:"omitempty"`
+	CDNClusterID uint   `form:"cdn_cluster_id" binding:"omitempty"`
 	Page         int    `form:"page" binding:"omitempty,gte=1"`
 	PerPage      int    `form:"per_page" binding:"omitempty,gte=1,lte=50"`
 	Status       string `form:"status" binding:"omitempty,oneof=active inactive"`
diff --git a/manager/types/oauth.go b/manager/types/oauth.go
index 98835948..c137b512 100644
--- a/manager/types/oauth.go
+++ b/manager/types/oauth.go
@@ -1,28 +1,44 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package types
 
 type OauthParams struct {
 	ID uint `uri:"id" binding:"required"`
 }
 
-type OauthPathParams struct {
-	OauthName string `uri:"oauth_name" binding:"required"`
-}
-
-type OauthBaseRquest struct {
-	Name         string `json:"name" binding:"required"`
+type CreateOauthRequest struct {
+	Name         string `json:"name" binding:"required,oneof=github google"`
+	BIO          string `json:"bio" binding:"omitempty"`
 	ClientID     string `json:"client_id" binding:"required"`
 	ClientSecret string `json:"client_secret" binding:"required"`
-	// scope list split by ','
-	Scopes      string `json:"scopes" binding:"omitempty"`
-	AuthURL     string `json:"auth_url" binding:"omitempty"`
-	TokenURL    string `json:"token_url" binding:"omitempty"`
-	UserInfoURL string `json:"user_info_url" binding:"omitempty"`
+	RedirectURL  string `json:"redirect_url" binding:"omitempty,url"`
 }
 
-type CreateOauthRequest struct {
-	OauthBaseRquest
+type UpdateOauthRequest struct {
+	Name         string `json:"name" binding:"omitempty,oneof=github google"`
+	BIO          string `json:"bio" binding:"omitempty"`
+	ClientID     string `json:"client_id" binding:"omitempty"`
+	ClientSecret string `json:"client_secret" binding:"omitempty"`
+	RedirectURL  string `json:"redirect_url" binding:"omitempty,url"`
 }
 
-type UpdateOauthRequest struct {
-	OauthBaseRquest
+type GetOauthsQuery struct {
+	Page     int    `form:"page" binding:"omitempty,gte=1"`
+	PerPage  int    `form:"per_page" binding:"omitempty,gte=1,lte=50"`
+	Name     string `form:"name" binding:"omitempty,oneof=github google"`
+	ClientID string `form:"client_id" binding:"omitempty"`
 }
diff --git a/manager/types/scheduler.go b/manager/types/scheduler.go
index f4af6c63..3f77e30a 100644
--- a/manager/types/scheduler.go
+++ b/manager/types/scheduler.go
@@ -50,5 +50,5 @@ type GetSchedulersQuery struct {
 	Location           string `form:"location" binding:"omitempty"`
 	IP                 string `form:"ip" binding:"omitempty"`
 	Status             string `form:"status" binding:"omitempty,oneof=active inactive"`
-	SchedulerClusterID uint   `json:"scheduler_cluster_id" binding:"omitempty"`
+	SchedulerClusterID uint   `form:"scheduler_cluster_id" binding:"required"`
 }
diff --git a/manager/types/settings.go b/manager/types/settings.go
deleted file mode 100644
index 994dae19..00000000
--- a/manager/types/settings.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package types
-
-type SettingParams struct {
-	Key string `uri:"key" binding:"required"`
-}
-type CreateSettingRequest struct {
-	Key   string `json:"key" binding:"required"`
-	Value string `json:"value" binding:"required"`
-}
-
-type UpdateSettingRequest struct {
-	Key   string `json:"key" binding:"required"`
-	Value string `json:"value" binding:"required"`
-}
-
-type GetSettingsQuery struct {
-	Page    int `form:"page" binding:"omitempty,gte=1"`
-	PerPage int `form:"per_page" binding:"omitempty,gte=1,lte=50"`
-}
diff --git a/manager/types/user.go b/manager/types/user.go
index 319a5857..8580d7bf 100644
--- a/manager/types/user.go
+++ b/manager/types/user.go
@@ -25,6 +25,18 @@ type SignInRequest struct {
 	Password string `json:"password" binding:"required,min=8,max=20"`
 }
 
+type OauthSigninParams struct {
+	Name string `uri:"name" binding:"required"`
+}
+
+type OauthSigninCallbackParams struct {
+	Name string `uri:"name" binding:"required"`
+}
+
+type OauthSigninCallbackQuery struct {
+	Code string `form:"code" binding:"required"`
+}
+
 type ResetPasswordRequest struct {
 	OldPassword string `json:"old_password" binding:"required,min=8,max=20"`
 	NewPassword string `json:"new_password" binding:"required,min=8,max=20"`


Test patch:
diff --git a/manager/permission/rbac/rbac_test.go b/manager/permission/rbac/rbac_test.go
index 9726e486..796d7b61 100644
--- a/manager/permission/rbac/rbac_test.go
+++ b/manager/permission/rbac/rbac_test.go
@@ -85,15 +85,15 @@ func TestHTTPMethodToAction(t *testing.T) {
 	}{
 		{
 			method:         "GET",
-			exceptedAction: "read",
+			exceptedAction: ReadAction,
 		},
 		{
 			method:         "POST",
-			exceptedAction: "*",
+			exceptedAction: AllAction,
 		},
 		{
 			method:         "UNKNOWN",
-			exceptedAction: "read",
+			exceptedAction: ReadAction,
 		},
 	}