{"id":36508788,"url":"https://github.com/llaoj/oauth2nsso","last_synced_at":"2026-01-23T07:38:53.414Z","repository":{"id":49854451,"uuid":"251270877","full_name":"llaoj/oauth2nsso","owner":"llaoj","description":"A standalone  OAuth2 \u0026 SSO server  based on go-oauth2","archived":false,"fork":false,"pushed_at":"2025-01-07T02:26:06.000Z","size":2131,"stargazers_count":197,"open_issues_count":7,"forks_count":53,"subscribers_count":7,"default_branch":"master","last_synced_at":"2026-01-12T05:01:34.783Z","etag":null,"topics":["go-oauth2","oauth2","oauth2-server","sso"],"latest_commit_sha":null,"homepage":"https://github.com/llaoj/oauth2nsso/blob/master/docs/demo.md","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/llaoj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-03-30T10:22:11.000Z","updated_at":"2026-01-05T15:23:54.000Z","dependencies_parsed_at":"2024-06-20T11:57:47.273Z","dependency_job_id":"289f6267-6a53-4f51-ad9b-5ee5ffab8239","html_url":"https://github.com/llaoj/oauth2nsso","commit_stats":null,"previous_names":["llaoj/oauth2"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/llaoj/oauth2nsso","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llaoj%2Foauth2nsso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llaoj%2Foauth2nsso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llaoj%2Foauth2nsso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llaoj%2Foauth2nsso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/llaoj","download_url":"https://codeload.github.com/llaoj/oauth2nsso/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llaoj%2Foauth2nsso/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28683894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T05:48:07.525Z","status":"ssl_error","status_checked_at":"2026-01-23T05:48:07.129Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go-oauth2","oauth2","oauth2-server","sso"],"created_at":"2026-01-12T02:33:08.623Z","updated_at":"2026-01-23T07:38:53.406Z","avatar_url":"https://github.com/llaoj.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OAuth2\u0026SSO\r\n\r\n## 项目介绍\r\n\r\n[llaoj/oauth2nsso](https://github.com/llaoj/oauth2nsso) 项目是基于 go-oauth2 打造的**独立**的 OAuth2.0 和 SSO 服务，提供了开箱即用的 OAuth2.0服务和单点登录SSO服务。开源一年多，获得了社区很多用户的关注，该项目多公司线上在用，其中包含上市公司。轻又好用，稳的一P。\r\n\r\n感谢:\r\n![sponsors](https://raw.githubusercontent.com/llaoj/oauth2nsso/master/docs/sponsors.png)\r\n\r\n## B站视频讲解\r\n\r\n [教你构建OAuth2.0和SSO单点登录服务(基于go-oauth2)](https://www.bilibili.com/video/BV1UA411v73P)\r\n\r\n## 单点登录(SSO)示例\r\n\r\n[单点登录(SSO)示例](docs/demo.md)\r\n\r\n## 动图演示\r\n\r\n授权码(authorization_code)流程 \u0026 单点登录(SSO)\r\n\r\n![authorization_code_n_sso](https://raw.githubusercontent.com/llaoj/oauth2nsso/master/docs/demo-pic/authorization_code_n_sso.gif)\r\n\r\n## 主要功能\r\n\r\n**实现了oauth2的四种工作流程**\r\n\r\n1. authorization_code\r\n2. implicit\r\n3. password\r\n4. client credentials\r\n\r\n**扩展功能**\r\n\r\n5. 资源端用的验证 access_token 接口 `/verify`\r\n6. 刷新 token 接口 `/refresh`\r\n7. 专门为 SSO 开发的客户端登出接口 `/logout`\r\n\r\n详情见[API说明](https://blog.llaoj.cn/docs/oauth2nsso/apis/)\r\n\r\n\r\n## 配置\r\n\r\n该项目的配置修改都是在配置文件中完成的，配置文件在启动应用的时候通过`--config=`标签进行配置.\r\n\r\n配置文件介绍如下：\r\n\r\n```yaml\r\n# session 相关配置\r\nsession:\r\n  name: session_id\r\n  secret_key: \"kkoiybh1ah6rbh0\"\r\n  # 过期时间\r\n  # 单位秒\r\n  # 默认20分钟\r\n  max_age: 1200\r\n\r\n# 用户登录验证方式\r\n# 支持: db ldap\r\nauth_mode: ldap\r\n\r\n# 数据库相关配置\r\n# 这里可以添加多个连接支持\r\n# 默认是 default 连接\r\ndb:\r\n  default:\r\n    type: mysql\r\n    host: string\r\n    port: 3306\r\n    user: 123\r\n    password: abc\r\n    dbname: oauth2nsso\r\n\r\nldap:\r\n  # 服务地址\r\n  # 支持 ldap ldaps\r\n  url: ldap://ldap.forumsys.com\r\n  # url: ldaps://ldap.rutron.net\r\n\r\n  # 查询使用的DN\r\n  search_dn: cn=read-only-admin,dc=example,dc=com\r\n  # 查询使用DN的密码\r\n  search_password: password\r\n  \r\n  # 基础DN\r\n  # 以此为基础开始查找用户\r\n  base_dn: dc=example,dc=com\r\n  # 查询用户的Filter\r\n  # 比如: \r\n  #   (\u0026(uid=%s)) \r\n  #   或 (\u0026(objectClass=organizationalPerson)(uid=%s))\r\n  #   其中, (uid=%s) 表示使用 uid 属性检索用户, \r\n  #   %s 为用户名, 这一段必须要有, 可以替换 uid 以使用其他属性检索用户名\r\n  filter: (\u0026(uid=%s))\r\n\r\n# 可选\r\n# redis 相关配置\r\n# 可以提供:\r\n# - 统一回话存储\r\n# - oauth2 client 存储\r\nredis:\r\n  default:\r\n    addr: 127.0.0.1:6379\r\n    password: \r\n    db: 0\r\n\r\n# oauth2 相关配置\r\noauth2:\r\n  # access_token 过期时间\r\n  # 单位小时\r\n  # 默认2小时\r\n  access_token_exp: 2\r\n  # 签名 jwt access_token 时所用 key\r\n  jwt_signed_key: \"k2bjI75JJHolp0i\"\r\n  \r\n  # oauth2 客户端配置\r\n  # 数组类型\r\n  # 可配置多客户端\r\n  client:\r\n\r\n      # 客户端id 必须全局唯一\r\n    - id: test_client_1\r\n      # 客户端 secret\r\n      secret: test_secret_1\r\n      # 应用名 在页面上必要时进行显示\r\n      name: 测试应用1\r\n      # 客户端 domain\r\n      # !!注意 http/https 不要写错!!\r\n      domain: http://localhost:9093\r\n      # 权限范围\r\n      # 数组类型\r\n      # 可以配置多个权限 \r\n      # 颁发的 access_token 中会包含该值 资源方可以对该值进行验证\r\n      scope:\r\n          # 权限范围 id 唯一\r\n        - id: all\r\n          # 权限范围名称\r\n          # 会在页面（登录页面）进行展示\r\n          title: \"用户账号、手机、权限、角色等信息\"\r\n\r\n    - id: test_client_2\r\n      secret: test_secret_2\r\n      name: 测试应用2 \r\n      domain: http://localhost:9094\r\n      scope:\r\n        - id: all\r\n          title: 用户账号, 手机, 权限, 角色等信息\r\n```\r\n\r\n\r\n## API列表\r\n\r\n### 1 authorization_code\r\n\r\n#### 1-1 获取授权code\r\n\r\n**请求方式**\r\n\r\n`GET` `/authorize`\r\n\r\n**参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|client_id|string|在oauth2 server注册的client_id,见配置文件[oauth2.client.id](http://rutron.net/docs/oauth2nsso/configuration/)|\r\n|response_type|string|固定值:`code`|\r\n|scope|string|权限范围,如:`str1,str2,str3`,str为配置文件中[oauth2.client.scope.id](http://rutron.net/docs/oauth2nsso/configuration/)的值 |\r\n|state|string|表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值|\r\n|redirect_uri|string|回调uri,会在后面添加query参数`?code=xxx\u0026state=xxx`,发放的code就在其中|\r\n\r\n**请求示例**\r\n\r\n```sh\r\n# 浏览器请求\r\nhttp://localhost:9096/authorize?client_id=test_client_1\u0026response_type=code\u0026scope=all\u0026state=xyz\u0026redirect_uri=http://localhost:9093/cb\r\n\r\n# 302跳转,返回code\r\nhttp://localhost:9093/cb?code=XUNKO4OPPROWAPFKEWNZWA\u0026state=xyz\r\n```\r\n\r\n#### 1-2 使用`code`交换`token`\r\n\r\n**请求方式**\r\n\r\n`POST` `/token`\r\n\r\n**请求头 Authorization**\r\n\r\n- basic auth\r\n- username: `client_id`\r\n- password: `client_secret`\r\n\r\n**Header**  \r\n`Content-Type: application/x-www-form-urlencoded`\r\n\r\n**Body参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|grant_type|string|固定值`authorization_code`|\r\n|code|string| 1-1 发放的code|\r\n|redirect_uri|string| 1-1 填写的redirect_uri|\r\n\r\n**Response返回示例**  \r\n\r\n```json\r\n{\r\n    \"access_token\": \"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIyMjIyMjIiLCJleHAiOjE1ODU3MTU1NTksInN1YiI6InRlc3QifQ.ZMgIDQMW7FGxbF1V8zWOmEkmB7aLH1suGYjhDdrT7aCYMEudWUoiCkWHSvBmJahGm0RDXa3IyDoGFxeMfzlDNQ\",\r\n    \"expires_in\": 7200,\r\n    \"refresh_token\": \"JG7_WGLWXUOW2KV2VLJKSG\",\r\n    \"scope\": \"all\",\r\n    \"token_type\": \"Bearer\"\r\n}\r\n```\r\n\r\n### 2 implicit\r\n\r\n资源请求方(client方)使用, \r\n多用于没有后端的应用, \r\n用户授权登录之后, 会直接向前端发送令牌(`access_token`)\r\n\r\n**请求方式**\r\n\r\n`GET` `/authorize`\r\n\r\n**参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|client_id|string|在 oauth2 server 注册的client_id|\r\n|response_type|string|固定值`token`|\r\n|scope|string|权限范围,同1-1中说明|\r\n|state|string|验证请求的标志字段|\r\n|redirect_uri|string|发放`code`用的回调uri,回调时会在uri后面跟上`?code=**\u0026state=###`|\r\n\r\n**请求示例**\r\n\r\n```sh\r\nhttp://localhost:9096/authorize?client_id=test_client_1\u0026response_type=token\u0026scope=all\u0026state=xyz\u0026redirect_uri=http://localhost:9093/cb\r\n```\r\n\r\n**返回示例**\r\n\r\n```sh\r\n# 302 跳转,返回 access_token\r\nhttp://localhost:9093/cb#access_token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0X2NsaWVudF8xIiwiZXhwIjoxNTkxNDI3OTMwLCJzdWIiOiJhZG1pbiJ9.RBYns9UnNYDHINSBzvHWHRzuKCpzKmsxUnKt30lntmGvXmVDoByZtlB0RHAVB59PHBlJNO_YUBZzC2odwCa8Tg\r\n\u0026expires_in=3600\u0026scope=all\u0026state=xyz\u0026token_type=Bearer\r\n```\r\n\r\n**注意**\r\n\r\n1. 这里会返回请求时设置的`state`, 请在进行下一步之前验证它, 防止请求被劫持或者篡改\r\n2. 这种方式把令牌直接传给前端，是很不安全的。因此，只能用于一些安全要求不高的场景，并且令牌的有效期必须非常短，通常就是会话期间（session）有效，浏览器关掉，令牌就失效了\r\n\r\n\r\n### 3 password\r\n\r\n资源请求方(client方)使用\r\n如果充分信任接入应用(client), 用户就可以直接把用户名密码给接入应用.\r\n接入应用使用用户账号密码申请令牌.\r\n\r\n**请求方式**\r\n\r\n`POST` `/token`\r\n\r\n**请求头 Authorization**\r\n\r\n- basic auth\r\n- username: `client_id`\r\n- password: `client_secret`\r\n\r\n**Header**  \r\n`Content-Type: application/x-www-form-urlencoded`\r\n\r\n**Body参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|grant_type|string|固定值`password`|\r\n|username|string|用户名|\r\n|password|string|用户密码|\r\n|scope|string|权限范围,同1-1中说明|\r\n\r\n**返回示例**  \r\n\r\n```json\r\n{\r\n    \"access_token\": \"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0X2NsaWVudF8xIiwiZXhwIjoxNTkxNDMyNzA3LCJzdWIiOiJhZG1pbiJ9.ECfUkCMUZE8I6GH3XTDcJnQgDryiRyyBhEHBW-dCxzFWaR-mvU5dsx3XV2bx-LWZzPJTBAQ3rB5QOb4BHjnBXw\",\r\n    \"expires_in\": 7200,\r\n    \"refresh_token\": \"AH-B00RKXTME9WXDPSBSTG\",\r\n    \"scope\": \"all\",\r\n    \"token_type\": \"Bearer\"\r\n}\r\n```\r\n\r\n### 4 client_credentials\r\n\r\n资源请求方(client方)使用\r\n使用在oauth2服务器注册的client_id 和 client_secret 获取 access_token,\r\n发出 API 请求时，它应将access_token作为 Bearer 令牌传递到 Authorization 请求头中。\r\n\r\n**请求方式**\r\n\r\n`POST` `/token`\r\n\r\n**请求头 Authorization**\r\n\r\n- basic auth\r\n- username: `client_id`\r\n- password: `client_secret`\r\n\r\n**Header**  \r\n\r\n`Content-Type: application/x-www-form-urlencoded`\r\n\r\n**Body参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|grant_type|string|固定值`client_credentials`|\r\n|scope|string|权限范围,同1-1中说明|\r\n\r\n**返回示例**  \r\n\r\n```json\r\n{\r\n    \"access_token\": \"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJlbWJlZGVkLWg1LWFwaSIsImV4cCI6MTU4OTk3NzQyNn0.Pu93fy0-gyiFqExBkCFAKTVJ1on_RpOSexzkHqczA6n6kB2_mOHbTMOyGK_Di7bHxZ3JqpZeyDoKQBtUe_T7jw\",\r\n    \"expires_in\": 7200,\r\n    \"token_type\": \"Bearer\"\r\n}\r\n```\r\n\r\n### 5 验证token\r\n\r\n**接口说明**\r\n\r\n这个接口是资源端使用的, \r\n用来验证 `access_token` `scope` 和 `domain` .\r\n\r\n**请求方式**\r\n\r\n`GET`  `/verify`\r\n\r\n**请求头 Authorization**\r\n\r\n- Bearer Token\r\n- Token: `access_token`\r\n\r\n**返回示例**  \r\n\r\n正确 Status Code: 200\r\n\r\nResponse Body:\r\n\r\n```json\r\n{\r\n  \"client_id\": \"test_client_1\",\r\n  \"domain\": \"http://127.0.0.1:9093\",\r\n  \"expires_in\": 7188,\r\n  \"scope\": \"all\",\r\n  \"user_id\": \"\"\r\n}\r\n```\r\n\u003e **注意:** 接口还会一起返回权限范围`scope` 和 client 的注册 domain, 这里推荐验证下, 请求方的身份和权限\r\n\r\n错误 Status Code: 400\r\n\r\nResponse Body: `invalid access token`\r\n\r\n### 6 刷新token\r\n\r\n刷新access_token, 使用refresh_token换取access_token\r\n\r\n**请求方式**\r\n\r\n`POST` `/token`\r\n\r\n**请求头 Authorization**\r\n\r\n- basic auth\r\n- username: `client_id`\r\n- password: `client_secret`\r\n\r\n**Header**\r\n\r\n`Content-Type: application/x-www-form-urlencoded`\r\n\r\n**Body参数说明**\r\n\r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|grant_type|string|固定值`refresh_token`|\r\n|refresh_token|string|之前获取的refresh_token|\r\n\r\n**返回示例**\r\n\r\n```json\r\n{\r\n    \"access_token\": \"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIyMjIyMjIiLCJleHAiOjE1ODU4MTc2MTMsInN1YiI6IjEifQ.yNpQIbklhtsSr5KEkJMAR4I30c85OEriYwAOpL_ukRBJ1qsSziT05HFN-kxVN1-qM18TzVEf8beCvugyhpgpsg\",\r\n    \"expires_in\": 7200,\r\n    \"refresh_token\": \"2AH_LQHPUYK8XML4LKMQKG\",\r\n    \"scope\": \"all\",\r\n    \"token_type\": \"Bearer\"\r\n}\r\n```\r\n\r\n### 7 logout\r\n\r\n专门为SSO开发, \r\n主要是销毁浏览器的会话, 退出登录状态, 跳转到指定链接(redirect_uri)\r\n\r\n**请求方式**\r\n\r\n`GET` `/logout?redirect_uri=xxx`\r\n\r\n**参数说明**  \r\n\r\n|参数|类型|说明|\r\n|-|-|-|\r\n|redirect_uri|string|退出登录后跳转到的地址,建议使用1-1所生成的地址, 需要urlencode|\r\n\r\n**请求示例**  \r\n\r\n```sh\r\nhttp://localhost:9096/logout?redirect_uri=http%3a%2f%2flocalhost%3a9096%2fauthorize%3fclient_id%3dtest_client_1%26response_type%3dcode%26scope%3dall%26state%3dxyz%26redirect_uri%3dhttp%3a%2f%2flocalhost%3a9093%2fcb\r\n```\r\n\r\n\r\n## 部署\r\n\r\n### 修改配置和完善代码\r\n\r\n克隆到代码之后，首先需要进行配置文件的修改和部分代码逻辑的编写：\r\n\r\n```sh\r\n# 克隆源码\r\ngit clone git@github.com:llaoj/oauth2nsso.git\r\ncd oauth2nsso\r\n\r\n# 根据实际情况修改配置\r\ncp config.example.yaml /etc/oauth2nsso/config.yaml\r\nvi /etc/oauth2nsso/config.yaml\r\n...\r\n\r\n# 如果使用 LDAP方式 验证用户, 直接修改配置文件即可\r\n# OR\r\n# 如果使用 数据库方式 验证用户, 需要修改源码\r\n# 主要修改登录部分逻辑:\r\n# 文件: model/user.go:21\r\n# 方法: Authentication()\r\n...\r\n```\r\n\r\n### 使用docker部署\r\n\r\n**[推荐]** 容器化部署比较方便进行大规模部署，是当下的趋势。需要本地有 docker 环境。\r\n\r\n```sh\r\n# 构建镜像\r\ndocker build -t \u003cimage:tag\u003e .\r\n\r\n# 运行\r\ndocker run --rm --name=oauth2nsso --restart=always -d \\\r\n-p 9096:9096 \\\r\n-v \u003cpath to config.yaml\u003e:/etc/oauth2nsso/config.yaml \\\r\n\u003cimage:tag\u003e\r\n```\r\n\r\n### 基于源码部署\r\n\r\n```sh\r\n# 在仓库根目录\r\n# 编译\r\ngo build -mod=vendor\r\n\r\n# 运行\r\n./oauth2nsso -config=/etc/oauth2nsso/config.yaml\r\n```\r\n\r\n## 客户端接入\r\n\r\n下面是用户第一次登录客户端(待接入应用)过程的时序图, 图中标明了 API 调用时机, 可以参考该流程接入SSO\r\n\r\n![uml1](docs/uml1.png)\r\n\r\n## 版本说明\r\n\r\n### v0.2.0\r\n\r\n该项目发布以来收到了很多朋友的关注，很多公司都将它应用到了一些比较重要的项目中。同时，也对该项目提出了很多要求。综合这些，开发了这个版本。同时希望朋友们互相交流，多提意见。\r\n\r\n这个版本主要有下面几个改动：\r\n\r\n1. 由于 go-oauth2.v3 版本安全性原因，将该包升级到 v4\r\n2. 丰富了可配置的项目\r\n3. 增加了容器化部署的脚本和相关文档\r\n4. 多了一些细节的优化\r\n5. 增加了错误页面\r\n6. 用户验证增加了LDAP支持\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllaoj%2Foauth2nsso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fllaoj%2Foauth2nsso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllaoj%2Foauth2nsso/lists"}