带有Firebase的Flutter JWT将GraphQLgraphql_flutter请求发送到具有“格式错误的授权标头”的Heroku Hasura

问题描述

我一直在寻找答案,但是在同时使用Flutter,Firebase和Hasura GraphQL的情况下,我还没有看到有关此问题的讨论。

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

下面的许多设置和代码均受到https://hasura.io/blog/build-flutter-app-hasura-firebase-part1/,第2部分和第3部分的教程以及https://github.com/snowballdigital/flutter-graphql的flutter graphql文档的启发。

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

HEADER:算法和令牌类型

{
    "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 UI中的Decode JWT工具显示此错误:

“声明密钥:'https://hasura.io/jwt/claims'未找到”

根据Hasura's documentation,令牌中应存在以下内容:

"https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": ["editor","user","mod"],"x-hasura-default-role": "user","x-hasura-user-id": "1234567890","x-hasura-org-id": "123","x-hasura-custom": "custom-value"
  }

在我使用的各种教程和文档中,我唯一需要定义hasura自定义声明的地方是在注册用户的Firebase云功能中:

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中缺少的信息是否与记录为“格式错误的授权标头”的服务器端错误相同,还是我需要设置其他标头(请参见下面的Flutter代码)。

这是Flutter中的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,);
  }
}

解决方法

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

  final HttpsCallable callable = CloudFunctions.instance
      .getHttpsCallable(functionName: 'registerUser')
        ..timeout = const Duration(seconds: 30);
  await callable.call(<String,dynamic>{
    'email': email,'password': password,});

(此代码段感谢https://hasura.io/blog/build-flutter-app-hasura-firebase-part3/

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...