Firebase JWT的Flatter将GraphQL(GraphQL_flatter)请求发送给Heroku Hasura,该请求具有;格式错误的授权标头“;

Firebase JWT的Flatter将GraphQL(GraphQL_flatter)请求发送给Heroku Hasura,该请求具有;格式错误的授权标头“;,firebase,flutter,graphql,jwt,hasura,Firebase,Flutter,Graphql,Jwt,Hasura,我一直在寻找答案,但我还没有看到在一起使用flatter、Firebase和Hasura-GraphQL的上下文中讨论这个问题 在Flatter中工作,利用Firebase身份验证后端,我正在获取一个用户JWT并将其传递给Heroku Hasura GraphQL端点(带有一个查询) 大部分设置和代码都遵循并受到了中的教程第2部分和第3部分以及中的颤振图SQL文档的启发 Firebase正在成功地将新用户记录添加到我的GraphQL数据库中。Firebase正在返回一个JWT,并在构建Graph

我一直在寻找答案,但我还没有看到在一起使用flatter、Firebase和Hasura-GraphQL的上下文中讨论这个问题

在Flatter中工作,利用Firebase身份验证后端,我正在获取一个用户JWT并将其传递给Heroku Hasura GraphQL端点(带有一个查询)

大部分设置和代码都遵循并受到了中的教程第2部分和第3部分以及中的颤振图SQL文档的启发

Firebase正在成功地将新用户记录添加到我的GraphQL数据库中。Firebase正在返回一个JWT,并在构建GraphQL客户端期间将其添加到GraphQL AuthLink中。这就是JWT的样子(隐藏个人信息):

标题:算法和令牌类型

{
    "alg": "RS256",
    "kid": "12809dd239d24bd379c0ad191f8b0edcdb9d3914",
    "typ": "JWT"
}

FULL PAYLOAD:DATA

{
    "iss": "https://securetoken.google.com/<firebase-app-id>",
    "aud": "<firebase-app-id>",
    "auth_time": 1598563214,
    "user_id": "iMovnQvpwuO8HiGOV82cYTmZRM92",
    "sub": "iMovnQvpwuO8HiGOV82cYTmZRM92",
    "iat": 1598635486,
    "exp": 1598639086,
    "email": "<user-email>",
    "email_verified": false,
    "firebase": {
        "identities": {
            "email": [
                "<user-email>"
            ]
        },
        "sign_in_provider": "password"
    }
}
在我使用的各种教程和文档中,我看到定义hasura自定义声明的唯一需要是在Firebase cloud函数中注册用户:

exports.registerUser = functions.https.onCall(async (data, context) => {

const email = data.email;
const password = data.password;
const displayName = data.displayName;

if (email === null || password === null || displayName === null) {
    throw new functions.https.HttpsError('unauthenticated', 'missing information');
}

try {
    const userRecord = await admin.auth().createUser({
        email: email,
        password: password,
        displayName: displayName
    });

    const customClaims = {
        "https://hasura.io/jwt/claims": {
            "x-hasura-default-role": "user",
            "x-hasura-allowed-roles": ["user"],
            "x-hasura-user-id": userRecord.uid
        }
    };

    await admin.auth().setCustomUserClaims(userRecord.uid, customClaims);
    return userRecord.toJSON();

} catch (e) {
    throw new functions.https.HttpsError('unauthenticated', JSON.stringify(error, undefined, 2));
}
}))

我对Firebase和JWT来说太陌生了,无法理解为什么自定义声明不在令牌中。我以为Firebase会给我一个嵌入了自定义声明的JWT,把它传递给Hasura后端就足够了

我的Heroku Hasura应用程序日志也显示此错误:

“格式错误的授权标头”,“代码”:“无效标头”

Firebase是否需要进一步配置,以便归还正确的索赔?JWT中缺少的信息是否与记录为“格式错误的授权头”的服务器端错误相同,或者我是否需要设置其他头(请参阅下面的颤振代码)

以下是颤振中的GraphQL配置代码:

import 'dart:async';

import 'package:dailyvibe/services/jwt_service.dart';
import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class AuthLink extends Link {
  AuthLink()
      : super(
          request: (Operation operation, [NextLink forward]) {
            StreamController<FetchResult> controller;

            Future<void> onListen() async {
              try {
                final String token = JWTSingleton.token;
                operation.setContext(<String, Map<String, String>>{
                  'headers': <String, String>{
                    'Authorization': '''bearer $token'''
                  }
                });
              } catch (error) {
                controller.addError(error);
              }

              await controller.addStream(forward(operation));
              await controller.close();
            }

            controller = StreamController<FetchResult>(onListen: onListen);

            return controller.stream;
          },
        );
}

class ConfigGraphQLClient extends StatefulWidget {
  const ConfigGraphQLClient({
    Key key,
    @required this.child,
  }) : super(key: key);
  final Widget child;

  @override
  _ConfigGraphQLClientState createState() => _ConfigGraphQLClientState();
}

class _ConfigGraphQLClientState extends State<ConfigGraphQLClient> {
  @override
  Widget build(BuildContext context) {
    final cache = InMemoryCache();

    final authLink = AuthLink()
        .concat(HttpLink(uri: 'https://<myapp>.herokuapp.com/v1/graphql'));

    final ValueNotifier<GraphQLClient> client = ValueNotifier(
      GraphQLClient(
        cache: cache,
        link: authLink,
      ),
    );

    return GraphQLProvider(
      client: client,
      child: CacheProvider(
        child: widget.child,
      ),
    );
  }
}
导入'dart:async';
导入“包:dailyvibe/services/jwt_service.dart”;
进口“包装:颤振/材料.省道”;
导入“包:graphql_颤振/graphql_颤振.dart”;
类AuthLink扩展了链接{
AuthLink()
:超级(
请求:(操作操作,[NextLink forward]){
流量控制器;
Future onListen()异步{
试一试{
最终字符串标记=JWTSingleton.token;
操作.setContext({
“标题”:{
“授权”:“持票人$token”
}
});
}捕获(错误){
控制器。加法器(错误);
}
等待控制器addStream(转发(操作));
等待控制器。关闭();
}
控制器=流控制器(onListen:onListen);
返回控制器.stream;
},
);
}
类ConfigGraphQLClient扩展StatefulWidget{
const ConfigGraphQLClient({
关键点,
@需要这个孩子,
}):super(key:key);
最后一个孩子;
@凌驾
_ConfigGraphQLClientState createState()=>\u ConfigGraphQLClientState();
}
类_ConfigGraphQLClientState扩展状态{
@凌驾
小部件构建(构建上下文){
最终缓存=InMemoryCache();
final authLink=authLink()
.concat(HttpLink(uri:'https://.herokuapp.com/v1/graphql'));
最终ValueNotifier客户端=ValueNotifier(
GraphQLClient(
缓存:缓存,
链接:authLink,
),
);
返回GraphQLProvider(
客户:客户,,
子:缓存提供程序(
child:widget.child,
),
);
}
}

回答我自己的问题:自定义声明不在我的JWT中,因为我没有从Flatter调用我的registerUser云函数。现在我是这样的:

  final HttpsCallable callable = CloudFunctions.instance
      .getHttpsCallable(functionName: 'registerUser')
        ..timeout = const Duration(seconds: 30);
  await callable.call(<String, dynamic>{
    'email': email,
    'password': password,
  });
final httpscalable callable=CloudFunctions.instance
.GetHttpScalable(函数名:“registerUser”)
..超时=常数持续时间(秒:30);
等待呼叫({
“电子邮件”:电子邮件,
“密码”:密码,
});
(此代码段是由于)


相反,我使用Firebase的
createUserWithEmailAndPassword(email:email,password:password)
方法,它不会创建自定义声明或触发云函数。

回答我自己的问题:自定义声明不在我的JWT中,因为我没有从Flatter调用我的registerUser云函数。现在我是这样的:

  final HttpsCallable callable = CloudFunctions.instance
      .getHttpsCallable(functionName: 'registerUser')
        ..timeout = const Duration(seconds: 30);
  await callable.call(<String, dynamic>{
    'email': email,
    'password': password,
  });
final httpscalable callable=CloudFunctions.instance
.GetHttpScalable(函数名:“registerUser”)
..超时=常数持续时间(秒:30);
等待呼叫({
“电子邮件”:电子邮件,
“密码”:密码,
});
(此代码段是由于)

相反,我使用的是Firebase的
createUserWithEmailAndPassword(email:email,password:password)
方法,它不会创建自定义声明或触发云功能