Skip to content

Instantly share code, notes, and snippets.

@vinsonzou
Last active March 22, 2024 04:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save vinsonzou/6ce186914171736becb5e35ebdf806e3 to your computer and use it in GitHub Desktop.
Save vinsonzou/6ce186914171736becb5e35ebdf806e3 to your computer and use it in GitHub Desktop.
Gitea通过飞书开放平台实现SSO
--[[
通过飞书登录获取用户email字段(用户飞书中必须添加email属性,否则无法登录!!!),如email地址为test@example.com,则gitea用户为test
登录后设定cookie,cookie有效期10小时
飞书开放平台的企业自建应用设置:
1、配置 安全设置-重定向URL,如此示例中的https://git.example.com
2、权限:获取用户邮箱信息
自动注册后,需管理员调整如下信息:
1、自定义名称
2、电子邮件地址
3、密码
]]
local require = require
local ngx = ngx
local ngx_re = require "ngx.re"
local cjson = require "cjson.safe"
local aes = require "resty.aes"
local http = require "resty.http"
local re_find = ngx.re.find
local json_encode = cjson.encode
local json_decode = cjson.decode
local app_id = "cli_xxx" -- 飞书开放平台企业自建应用app_id
local app_secret = "xxx" -- 飞书开放平台企业自建应用app_secret
local cookie_key = 'xxx' -- 自定义
local sso_status = ngx.var.cookie_sso_status
local token = ngx.shared.token
-- only for git clone(http)
if re_find(ngx.var.request_uri, [[(git-upload-pack|git-receive-pack)]], "jo") then
return
end
--------------------------
local function getToken()
local tenant_access_token = token:get("feishu")
if tenant_access_token then
return tenant_access_token
else
local url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal'
local payload = {app_id = app_id, app_secret = app_secret}
local httpc = http.new()
local res, err = httpc:request_uri(url, {
method = "POST",
body = json_encode(payload),
headers = {
["Content-Type"] = "application/json; charset=utf-8",
}
})
if not res then
ngx.log(ngx.ERR, 'failed to request: ', err)
return
end
if res.status == 200 then
local data = json_decode(res.body)
local code = data['code']
if code and code == 0 then
local tenant_access_token = data['tenant_access_token']
token:set("feishu", tenant_access_token, 6000)
return tenant_access_token
end
end
end
end
if sso_status then
local aes_128_cbc_md5 = aes:new(cookie_key)
local auth_user = aes_128_cbc_md5:decrypt(ngx.decode_base64(sso_status))
ngx.var.user = auth_user
else
local args = ngx.req.get_uri_args()
local code = args.code
if not code then
local redirect_url = "https://open.feishu.cn/open-apis/authen/v1/authorize?app_id=" .. app_id .. "&redirect_uri=https://git.example.com&scope=contact:user.email:readonly"
return ngx.redirect(redirect_url)
else
-- 修复Chrome 123无法登录问题(过滤OPTIONS请求,仅处理GET请求【因code只能被使用一次】)
local method = ngx.req.get_method()
if method == "OPTIONS" then
return ngx.exit(ngx.HTTP_OK)
end
local tenant_access_token = getToken()
local url = 'https://open.feishu.cn/open-apis/authen/v1/access_token'
local payload = {grant_type = "authorization_code", code = code}
local httpc = http.new()
local res, err = httpc:request_uri(url, {
method = "POST",
body = json_encode(payload),
headers = {
["Authorization"] = "Bearer ".. tenant_access_token,
["Content-Type"] = "application/json; charset=utf-8",
}
})
if not res then
ngx.log(ngx.ERR, 'failed to request: ', err)
return
end
if res.status == 200 then
local data = json_decode(res.body)
local code = data['code']
if code and code == 0 then
local display_name = data['data']['name']
local email = data['data']['email']
local from, to, err = re_find(email, "^[a-z0-9]+@example.com$", "jo")
if from then
local res, err = ngx_re.split(email, "@")
local auth_user = res[1]
ngx.var.user = auth_user
local aes_128_cbc_md5 = aes:new(cookie_key)
local encrypted = aes_128_cbc_md5:encrypt(auth_user)
ngx.header['Set-Cookie'] = 'sso_status=' .. ngx.encode_base64(encrypted) .. '; domain=example.com; path=/; SameSite=Lax; Secure; HttpOnly; Expires=' .. ngx.cookie_time(ngx.time()+36000)
ngx.log(ngx.ALERT, 'sso success: ', auth_user)
else
ngx.log(ngx.ERR, 'sso fail: invaild email')
return
end
else
ngx.log(ngx.ERR, 'sso fail: ', res.body)
return
end
else
ngx.log(ngx.ERR, 'sso request fail: ', res.body)
return
end
end
end
upstream git_backend {
server 127.0.0.1:3000;
keepalive 128;
}
lua_shared_dict token 1m;
server {
listen 443 ssl http2;
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:20m;
server_name git.example.com;
access_log logs/git.access.log main;
location / {
resolver local=on valid=30s ipv6=off;
lua_ssl_verify_depth 1;
lua_ssl_trusted_certificate /etc/pki/tls/certs/ca-bundle.crt;
set $user '';
access_by_lua_file lua/sso_feishu.lua;
proxy_set_header X-WEBAUTH-USER $user;
proxy_pass http://git_backend/;
proxy_http_version 1.1;
proxy_set_header Connection "";
client_max_body_size 10M;
}
}
@vinsonzou
Copy link
Author

Gitea后端调整配置如下:
[security]
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER

[service]
ENABLE_REVERSE_PROXY_AUTHENTICATION = true
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = true

@vinsonzou
Copy link
Author

升级至Chrome 123后,cookie必须添加SameSite属性,否则无法设定cookie。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment