OAUTH2 基础 - 一场噩梦 :-(

大家好,

我尝试设置 OAUTH2 已经两天了,但遇到了一些问题。

我按照文档配置了一切,但一直收到这个错误:

(oauth2_basic) Authentication failure! invalid_scope: OmniAuth::Strategies::OAuth2::CallbackError, invalid_scope | Unknown/invalid scope(s)
Started GET "/auth/failure?message=invalid_scope&origin=<censored>%2Latest&strategy=oauth2_basic" for 10.153.107.106 at 2024-11-27 05:30:33 +0000
Processing by Users::OmniauthCallbacksController#failure as HTML
  Parameters: {"message"=>"invalid_scope", "origin"=>"<censored>/latest", "strategy"=>"oauth2_basic"}

我的论坛配置(关于范围)如下:

身份提供商 (IdP) 的配置如下:

有人有什么提示,如何进一步调试这个问题吗?

有没有可以用来测试,或者更好的调试输出的 curl 命令(我已经开启了详细调试选项)?

我快被这个问题逼疯了 :frowning:

谢谢!

WS

更新:

我刚把选项改回只写“scope”

但还是不行 :frowning:

2 个赞

注意到我们的 IdP 在其 ui 上范围设置错误……

openId 在 ui 上
openid 是正确的 :slight_smile:
再进一步,但我现在遇到了超时

image

在此调用之后


OAuth2 调试:请求 POST https://<myAuthProvider/auth/oauth2/realms/root/realms/<realm>/access_token

标头:
--- !ruby/hash-with-ivars:Faraday::Utils::Headers
ivars:
  :@names:
    user-agent: User-Agent
    content-type: Content-Type
elements:
  User-Agent: Faraday v2.12.1
  Content-Type: application/x-www-form-urlencoded


正文:
---
client_id: <client-id>
client_secret: <client-secret>
grant_type: authorization_code
code: <code
redirect_uri: https://<myDiscourse>/auth/oauth2_basic/callback

以…结束

但由于获取代码的授权调用已通过,因此不可能是网络问题,对吗?

1 个赞

当我使用 curl 从 discourse docker 容器内部发出调用时

curl --request POST \
  --url https://<IdP address>/auth/oauth2/realms/root/realms/<realm>/access_token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=<clientId> \
  --data client_secret=<secret> \
  --data grant_type=authorization_code \
  --data code=<code \
  --data redirect_uri=https://<discourse>/auth/oauth2_basic/callback

我收到此响应

{
  "access_token": "<token>",
  "refresh_token": "<refresh_token>",
  "scope": "openid profile email",
  "id_token": "<censored>",
  "token_type": "Bearer",
  "expires_in": 7199
}

这可能是因为这个原因吗?

image

这些字段不在响应中 :frowning:

2 个赞

没有人? :frowning:

1 个赞

我对这个话题非常感兴趣,因为我正在研究为我的社区创建第一个论坛页面所需的精力,然后逐渐扩展到我孩子们的学校。我被来自不同平台的各种通知淹没了。
减少进入论坛页面的障碍至关重要,因此我想提供一些帮助。我对 Ruby 是新手,所以只能提供一些我找到的资源以及关于这些资源的建议。

你可能已经看过这个了,但你可能想查看 GitHub 上 discourse-openid-connect 的仓库

该仓库有 37 位贡献者,我想象其中一位能够帮助你回答你的问题。

我希望这有帮助,因为这是一个很棒的问题。

1 个赞

@thecatfix

看起来很有希望。我将立即尝试并尽快提供反馈。
可惜我今天没时间。也许我能在周末处理它。

非常感谢,祝您周末愉快!

嘿,

我等不及了,今天就测试了一下,但我们EAM部门的人搞砸了这个发现。它不起作用,我收到了无效客户端-身份验证失败的消息,尽管我百分之百确定凭据是有效的(用curl测试过)。
所以我又回到了起点。

有趣的是,我一直在收到请求到授权端点的超时错误,而且速度很快。

Faraday::TimeoutError (Net::ReadTimeout with #<TCPSocket:(closed)>)

我发现了这个,我想知道,在Discourse中是否也有类似的情况?

我完全不懂Ruby,所以我需要帮助。

我想调试Discourse端处理的每一个请求。有可能吗?
我还想过安装一个本地代理来拦截调用,但在深入研究之前,我想问一下是否有更简单的方法 :slight_smile:

您是想查看原始 HTTP 流量还是能够深入代码?

原始流量……

带有标头和有效载荷的请求……

有没有可能实现?

最简单的方法是从容器内部进行操作;您可以通过进入容器并运行以下命令来拦截和打印 nginx 和 Discourse 之间的请求:

apt-get update && apt-get -y install scapy
scapy

# 在 scapy 提示符下,粘贴以下内容:
class Callback:
  def __init__(self):
    self.last = None
  def prn(self, p):
    if p != self.last: # lo 上的 pcap 会捕获两次
      self.last = p
      p.hide_defaults()
      print(repr(p)) # 此行打印数据包,保留或删除它
      if scapy.packet.Raw in p.layers():
        try:
          print(p.load.decode())
        except:
          print(p.load)

sniff(filter="port 3000", iface="lo", prn=Callback().prn)
1 个赞

太棒了,谢谢……我一直在寻找这样的工具!

嗯……但是
看起来 discourse 正在收到回调,然后……
据我理解,它应该联系 userinfoendpoint 并使用检索到的代码获取 userinfo,但我遇到了 HTTP 500 错误。


GET /auth/oauth2_basic/callback?code=_B1HRB1e6kZKc8nuGLkzGC8&iss=https%3A%2F%2F%3CmyAuthDomain%3E%3A443%2Fauth%2Foauth2%2Frealms%2Froot%2Frealms%2Fintranetrealm&state=544801ae7e8262ea1667ea7531487f28e83aae232d5182b4&client_id=eadaa55a-1697-494a-8fg5-bb1137c68caa HTTP/1.0
Host: <discourseHost>
X-Request-Start: t=1732956951.485
X-Real-IP: 10.111.101.84
X-Forwarded-For: 10.131.101.84
X-Forwarded-Proto: https
Connection: close
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-fetch-site: same-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: https://discourseHost/
accept-encoding: gzip, deflate, br, zstd
accept-language: de-DE,de;q=0.9
priority: u=0, i
cookie: lbwdn=01; win=teUNUPSc8oibB......

<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=52 id=7742 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdf83 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33574 seq=271589520 ack=3955524768 dataofs=8 flags=A window=499 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962821542, 3962821542))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=281 id=55898 flags=DF frag=0 ttl=127 proto=tcp chksum=0x2282 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33530 seq=491394 ack=1852886175 dataofs=8 flags=PA window=507 chksum=0xff0d options=[('NOP', None), ('NOP', None), ('Timestamp', (3962823893, 3962816870))] |<Raw  load='HTTP/1.1 500 Internal Server Error\r\nDate: Sat, 30 Nov 2024 08:55:53 GMT\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 658\r\nX-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5\r\nX-Runtime: 10.078459\r\n\r\n' |>>>
HTTP/1.1 500 Internal Server Error
Date: Sat, 30 Nov 2024 08:55:53 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 658
X-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5
X-Runtime: 10.078459

.....

<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=52 id=15439 flags=DF frag=0 ttl=127 proto=tcp chksum=0xc172 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=33574 dport=3000 seq=3955524768 ack=271589749 dataofs=8 flags=A window=511 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=710 id=7744 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdcef src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33574 seq=271589749 ack=3955524768 dataofs=8 flags=PA window=512 chksum=0xbb options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |<Raw  load='<!DOCTYPE html>\n<html>\n<head>\n  <title>Oops - Error 500</title>\n  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n</head>\n<body>\n    <h1>Oops</h1>\n    <p>The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.</p>\n    <p>Detailed information about the error was logged, and an automatic notification generated. We\'ll take a look at it.</p>\n    <p>No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site\'s feedback category.</p>\n</body>\n</html>\n' |>>>>
<!DOCTYPE html>
<html>
<head>
  <title>Oops - Error 500</title>
.....

日志中打印了这些行
显示网络错误和超时,包括 HTTP 标头和错误详细信息的屏幕截图。(由 AI 标注)

在我看来,Discourse 在处理回调时似乎遇到了问题,现在我需要更深入地了解 Discourse 幕后发生的情况。

尽管我开启了所有我知道的调试模式,但日志中没有任何提示。

嗯,我真的被这个卡住了,这太令人沮丧了 :frowning:

嘿,各位,

我现在进展更大了 :slight_smile:

这与我们的 EAM 基础设施有关……

不过,我现在遇到了下一个问题 :smiley:

我的 userinfotoken 看起来是这样的:


{
  "company-i": "A1",
  "accounting-code": "5806",
  "given_name": "Mister",
  "family_name": "Bean",
  "name": "Mister Bean",
  "departmentnumber": "Covert Operations",
  "salutation": "Dude",
  "description": "Some description",
  "preferredlanguage": "DE",
  "inumber": "723jfio-7zwe8489",
  "employeenumber": "36484332",
  "employeetype": "BigCompany",
  "uid": "f57383",
  "adupn": "f57383@europe.bigcom.corp",
  "uniqueuid": "f57383",
  "uniqueuidq": "f57383",
  "loginname": "f57383",
  "email": "Mister.Bean@bigcom.corp",
  "sub": "f57383",
  "subname": "f57383"
}

并且我在日志中收到此错误:


ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "provider_uid" of relation "user_associated_accounts" violates not-null constraint
DETAIL:  Failing row contains (6, oauth2_basic, null, null, 2024-12-03 13:06:49.831182, {"name": "Mister Bean", "email": "Mister.Bean@bigcom.corp", "user..., {"token": "dszghsdhsdfoph", "expires": true, "expir..., {}, 2024-12-03 13:06:49.831362, 2024-12-03 13:06:49.831362).

Discourse 似乎无法将 userinfoendpoint 的响应映射到其内部名称。

我尝试通过 UI 进行设置:

但可能设置错了。

有人能给点提示如何设置吗?

谢谢,祝好,

WS

oauth2 json user id path 是否已填?

请确保这些值已设置:https://meta.discourse.org/t/configure-sign-up-and-log-in-with-auth0-using-the-oauth2-basic-plugin/64633#configure-discourse-3

你好,

谢谢你的回复。

看来我的提供商没有发送 provider_name 和 provider_uid 字段,因此 discourse 尝试在没有这些字段的情况下创建条目,并导致 NotNull 冲突。
但是,这难道不应该是插件(oauth2_basic)的责任,(如果远程 IdP 没有提供这些值)来填充这些值吗?
我期望类似

provider_name: “oauth2_basic”
provider_uid: “1234”

这样的值能自动用于每次用户登录…… :frowning:

provider_name 固定为 oauth2_basicprovider_uid 将是您在 oauth2 json user id path 中填入的任何内容。
您只需将此站点设置设置为基于您的提供商返回的内容的 ID 路径。该插件使用此 json 路径来填充 provider_uid,因此如果它当前为空或是一个不存在的路径,则值为 nil。
从您上面的 json 来看,该值应该是 sub

2 个赞

但我已经这样做了……

你说得对,路径应该是“sub”,但我在配置中是这样设置的

等等……你说的没错,是我太笨了 :frowning:

设置错误……

现在可以了!

2 个赞

有没有办法将这些设置保存到某个地方 :smiley: \n可以只备份配置设置吗?\n\n它们在数据库里吗?

太好了。

所有站点设置都保存在数据库中。假设您有 rails console 访问权限,以下命令应能显示设置列表。

SiteSetting.where("name LIKE 'oauth2_%'").pluck(:name, :value)

您也可以直接创建一个备份。