为 Flutter 中的 Firebase 身份验证设置自定义声明

And*_*rew 3 jwt firebase-authentication flutter google-cloud-functions jwt-auth

我正在为应用程序使用Firebase身份验证,但作为用户创建的一部分,我需要设置一些自定义声明。

我编写了一个云函数来在创建用户时设置声明:

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp(functions.config().firebase);

    // On sign up.
    exports.processSignUp = functions.auth.user().onCreate(user => {

    let customClaims;

    // Set custom user claims on this newly created user.
    return admin.auth().setCustomUserClaims(user.uid, {
            'https://hasura.io/jwt/claims': {
                'x-hasura-default-role': 'user',
                'x-hasura-allowed-roles': ['user'],
                'x-hasura-user-id': user.uid
            }
        })
        .then(() => {
            // Update real-time database to notify client to force refresh.
            const metadataRef = admin.database().ref("metadata/" + user.uid);
            // Set the refresh time to the current UTC timestamp.
            // This will be captured on the client to force a token refresh.
            return metadataRef.set({
                refreshTime: new Date().getTime()
            });
        })
        .then(() => {
            return admin.auth().getUser(user.uid);
        })
        .then(userRecord => {
            console.log(userRecord);
            return userRecord.toJSON();

        })
        .catch(error => {
            console.log(error);
        });
});
Run Code Online (Sandbox Code Playgroud)

当我打印到控制台 userRecord 时,我可以看到自定义声明设置正确。

然后在 flutter 中,我从创建的用户那里获得了令牌,但它似乎没有附加自定义声明。

我正在使用此代码创建用户并在 flutter 中打印声明

    Future<FirebaseUser> signUp({String email, String password}) async {
    final FirebaseUser user = (await auth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    )).user;

    IdTokenResult result = await (user.getIdToken(refresh: true));
    print('claims : ${result.claims}');

    return user;
  }
Run Code Online (Sandbox Code Playgroud)

如果我在 jwt 调试器中检查令牌本身,我可以看到它没有自定义声明。一旦设置了声明,我是否需要一些额外的步骤来尝试获取更新的令牌?我试过了user.reload()user.getIdToken(refresh: true)但他们似乎没有帮助。

关于如何获取具有自定义声明的令牌的任何想法?

And*_*rew 7

为了将来参考,我设法根据道格的建议进行了这项工作。

这是我的 firebase sdk 管理功能。

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const firestore = admin.firestore();
const settings = {timestampsInSnapshots: true};
firestore.settings(settings);


// On sign up.
exports.processSignUp = functions.auth.user().onCreate(async user => {

// Check if user meets role criteria:
// Your custom logic here: to decide what roles and other `x-hasura-*` should the user get
let customClaims;
// Set custom user claims on this newly created user.

return admin.auth().setCustomUserClaims(user.uid, {
    'https://hasura.io/jwt/claims': {
    'x-hasura-default-role': 'user',
    'x-hasura-allowed-roles': ['user'],
    'x-hasura-user-id': user.uid
}
  })
.then(async () => {
    await firestore.collection('users').doc(user.uid).set({
        createdAt: admin.firestore.FieldValue.serverTimestamp()
    });
 })
 .catch(error => {
    console.log(error);
 });
});
Run Code Online (Sandbox Code Playgroud)

然后在事物的颤动方面

Future<FirebaseUser> signUp({String email, String password}) async {
    final FirebaseUser user = (await auth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    )).user;
    currentUser = user;

    await waitForCustomClaims();
    return user;
}

Future waitForCustomClaims() async {
    DocumentReference userDocRef = 
    Firestore.instance.collection('users').document(currentUser.uid);
    Stream<DocumentSnapshot> docs = userDocRef.snapshots(includeMetadataChanges: false);

    DocumentSnapshot data = await docs.firstWhere((DocumentSnapshot snapshot) => snapshot?.data !=null && snapshot.data.containsKey('createdAt'));  
    print('data ${data.toString()}');

    IdTokenResult idTokenResult = await (currentUser.getIdToken(refresh: true));
    print('claims : ${idTokenResult.claims}');
}
Run Code Online (Sandbox Code Playgroud)

希望这会帮助其他想要做类似事情的人。


Dou*_*son 6

您显示的代码可能会在创建帐户后过早地尝试获取自定义声明。调用 后,该函数将需要几秒钟才能触发auth.createUserWithEmailAndPassword。它是异步运行的,完全不会阻碍用户创建的过程。因此,您需要以某种方式等待函数完成,然后再调用user.getIdToken(refresh: true).

这正是我在这篇博文中解决的问题。我提供的解决方案如下:

  1. 客户端:创建用户
  2. 客户端:等待在 Firestore 中创建具有用户 UID 的文档
  3. 服务器:Auth onCreate 函数触发器
  4. 服务器:函数完成它的工作
  5. 服务器:最后,函数使用新用户的 UID 将数据写入新文档
  6. 客户端:数据库侦听器在创建文档时触发

然后,您将在客户端上添加更多步骤以在它看到新文档后刷新 ID 令牌。

帖子中给出的代码适用于 web/javascript,但该过程适用于任何客户端。您只需要让客户端等待该功能完成,Firestore 是一个方便的传递该信息的地方,因为客户端可以实时收听它。

另请阅读这篇文章,了解根据写入 Firestore 文档的声明,让客户端立即刷新其令牌的方法。

最重要的是,您需要在客户端和服务器之间同步大量代码。