Swift iOS调用Auth0保护的NestJS接口返回401且无日志问题求助
问题描述
我有一套使用Auth0做鉴权的NestJS后端服务,通过react-admin携带请求头Authorization: Bearer ACCESS_TOKEN可以正常访问后端接口。
但我在Swift iOS应用中调用同套后端接口时遇到了问题:我已经完成用户登录流程,可以正常获取用户资料,但发起后端请求时返回401 Unauthorized错误,且Auth0后台日志无对应报错记录。
相关请求代码如下:
let path = "\(baseURL)\(endpoint.rawValue)" guard let url = URL(string: path) else { preconditionFailure("Bad URL") } var headers: [String:String] = [:] headers["Content-Type"] = "application/json" // if access token is set then set Authorization headers if (accessToken != nil) { headers["Authorization"] = "Bearer \(accessToken!)" print("Bearer \(accessToken!)") } var request = URLRequest(url: url) request.httpMethod = "\(method)" request.allHTTPHeaderFields = headers // check if body exists if (body != nil) { request.httpBody = body! } let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in guard error == nil else { completion(.failure(.serverError)); return } do { guard let data = data else { completion(.failure(.serverError)); return } guard let object : [[String: AnyObject]] = try JSONSerialization.object(with: data) as? [[String: AnyObject]] else { print("Unable to convert from data") return } guard let json = try? JSONSerialization.data(withJSONObject: object, options: .prettyPrinted) else { print("Unable to prettify") return } guard let jsonString = String(data: json, encoding: .utf8) else { print("Unable to convert to string") return } print("JSON: \(jsonString)") completion(Result.success(object)) } catch { completion(Result.failure(.parsingError)) } } dataTask.resume()
其中baseURL是指向我的NestJS后端的字符串,endpoint是接口枚举,例如\user。
我使用Proxyman抓包确认请求已到达对应端点,且请求头配置正确,截图如下:
另外我使用Postman可以正常登录并请求受保护数据,截图如下:
问题更新
我对react-admin请求成功的JWT和iOS请求失败的JWT做了解码,发现二者存在以下差异:
- 成功请求的JWT中
aud字段为数组,包含Auth0上注册的API标识以及Auth0用户信息端点 - 成功请求的JWT中存在
azp字段,值为我的clientID,失败请求的JWT无该字段 - 失败请求的JWT中
aud字段值为clientID - 失败请求的JWT缺失
scope和permissions字段。
问题原因
你解码JWT得到的差异已经直接指向了根因:iOS端请求Access Token时没有传入正确的audience参数,导致Auth0返回的令牌仅适配Auth0自身的用户信息接口,无法通过你后端服务的JWT校验。
Auth0的令牌颁发逻辑里,如果你请求令牌时没有传入你在Auth0后台注册的后端API的唯一标识符作为audience参数,返回的令牌aud字段会是你的应用Client ID,这种令牌只能用来调用Auth0自带的用户信息接口,不能用于自定义后端API的鉴权。你的NestJS服务校验JWT时会因为audience不匹配直接返回401,这个校验逻辑在你服务端执行,所以Auth0后台不会留下报错记录。
解决方案
- 找到iOS端Auth0登录、获取令牌的代码,在调用Auth0 SDK的授权、获取凭证方法时,增加
audience参数,参数值填你在Auth0后台注册API时设置的Identifier(也就是成功JWT的aud数组里的自定义API标识)。示例修改代码如下:
Auth0 .webAuth() .audience("YOUR_BACKEND_API_IDENTIFIER") // 替换为你自己的API标识 .scope("openid profile email 你的自定义权限scope") // 补充你后端需要的权限范围 .start { result in // 原有登录处理逻辑 }
- 如果你的应用有用到静默刷新令牌的逻辑,也要在对应的获取凭证方法中加入同样的
audience参数,保证刷新得到的令牌也符合后端校验要求。 - 修改完成后重新获取令牌,解码确认
aud字段包含你的后端API标识,且scope、permissions字段符合预期后,再发起后端请求即可正常通过鉴权。
内容的提问来源于stack exchange,提问作者Kenneth P. Hough

