15

Facebook の公式見解は、Relay は「意図的に認証メカニズムにとらわれない」というものです。Relay リポジトリのすべての例では、認証とアクセス制御は別の問題です。実際には、この分離を実装する簡単な方法は見つかりませんでした。

viewerRelay リポジトリで提供されている例にはすべて、ユーザーが 1 人であることを前提としたフィールドを持つルート スキーマがあります。そして、そのユーザーはすべてにアクセスできます。

ただし、実際には、アプリケーションには多くのユーザーがいて、各ユーザーは各ノードへのアクセス レベルが異なります。

JavaScript に次のスキーマがあるとします。

export const Schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'Query',
        fields: () => ({
            node: nodeField,
            user: {
                type: new GraphQLObjectType({
                    name: 'User',
                    args: {
                        // The `id` of the user being queried for
                        id: { type: new GraphQLNonNull(GraphQLID) },
                        // Identity the user who is querying
                        session: { type: new GraphQLInputObjectType({ ... }) },
                    },
                    resolve: (_, { id, session }) => {
                        // Given `session, get user with `id`
                        return data.getUser({ id, session });
                    }
                    fields: () => ({
                        name: {
                            type: GraphQLString,
                            resolve: user => {
                                // Does `session` have access to this user's
                                // name?
                                user.name
                            }
                        }
                    })
                })
            }
        })
    })
});

一部のユーザーは、クエリを実行するユーザーの観点から完全に非公開です。他のユーザーは、特定のフィールドのみを照会ユーザーに公開する場合があります。そのため、ユーザーを取得するには、クライアントはクエリ対象のユーザー ID を提供するだけでなく、アクセス制御が行われるように自分自身を識別する必要もあります。

これは、アクセスを制御する必要性がグラフを少しずつ下るにつれて、すぐに複雑になるようです。

さらに、ルート クエリごとにアクセスを制御する必要がありますnodeField。すべてのノードが を実装していることを確認する必要がありますnodeInterface

これらはすべて、多くの反復作業のように思えます。これを単純化するための既知のパターンはありますか? 私はこれについて間違って考えていますか?

4

3 に答える 3

7

アプリケーションが異なれば、アクセス制御の形式に対する要件も大きく異なるため、基本的な Relay フレームワークや GraphQL リファレンス実装に何かを組み込むことはおそらく意味がありません。

私がかなり成功したアプローチは、プライバシー/アクセス制御をデータ モデル/データ ローダー フレームワークに組み込むことです。オブジェクトをロードするたびに、ID でロードするだけでなく、ビューアのコンテキストも提供します。ビューアがオブジェクトを見ることができない場合、オブジェクトの存在が漏れるのを防ぐために、オブジェクトが存在しないかのように読み込まれません。オブジェクトはビューアー コンテキストも保持し、特定のフィールドは、オブジェクトから返される前にチェックされるアクセスが制限されている場合があります。これを下位レベルのデータ読み込みメカニズムに組み込むことで、上位レベルの製品 / GraphQL コードのバグによってプライベート データが漏洩しないようにすることができます。

具体的な例として、あるユーザーが私をブロックしたために、そのユーザーに会うことを許可されていない場合があります。あなたは彼とは友達ではないので、彼に会うことは許されるかもしれませんが、彼の電子メールは許されません。

次のようなコードで:

var viewer = new Viewer(getLoggedInUser());
User.load(id, viewer).then(
  (user) => console.log("User name:", user.name),
  (error) => console.log("User does not exist or you don't have access.")
)

GraphQL レベルで可視性を実装しようとすると、情報が漏洩する可能性が高くなります。Facebook の GraphQL 実装でユーザーにアクセスする多くの方法を考えてみてください。

node($userID) { name }
node($postID) { author { name } }
node($postID) { likers { name } }
node($otherUserID) { friends { name } }

これらのクエリはすべてユーザーの名前を読み込む可能性があり、ユーザーがあなたをブロックしている場合、ユーザーまたはその名前を返すものはありません。これらすべてのフィールドにアクセス制御を設定し、どこでもチェックを忘れないことは、どこかでチェックを逃すためのレシピです。

于 2015-11-07T01:00:03.633 に答える
4

rootValueスキーマに対してクエリが実行されるときに実行エンジンに渡されるGraphQL を利用すると、認証の処理が簡単であることがわかりました。この値は、すべての実行レベルで使用でき、アクセス トークンや現在のユーザーを識別するものを格納するのに役立ちます。

Express-graphqlミドルウェアを使用している場合は、GraphQL ミドルウェアの前にあるミドルウェアにセッションをロードし、そのセッションをルート値に配置するように GraphQL ミドルウェアを構成できます。

function getSession(req, res, next) {
  loadSession(req).then(session => {
    req.session = session;
    next();
  }).catch(
    res.sendStatus(400);
  );
}

app.use('/graphql', getSession, graphqlHTTP(({ session }) => ({
  schema: schema,
  rootValue: { session }
})));

このセッションは、スキーマの任意の深さで利用できます。

new GraphQLObjectType({
  name: 'MyType',
  fields: {
    myField: {
      type: GraphQLString,
      resolve(parentValue, _, { rootValue: { session } }) {
        // use `session` here
      }
    }
  }
});

これを「ビューア指向」のデータ読み込みと組み合わせて、アクセス制御を実現できます。この種のデータ読み込みオブジェクトの作成を支援し、バッチ処理とキャッシングを提供するhttps://github.com/facebook/dataloaderを確認してください。

function createLoaders(authToken) {
  return {
    users: new DataLoader(ids => genUsers(authToken, ids)),
    cdnUrls: new DataLoader(rawUrls => genCdnUrls(authToken, rawUrls)),
    stories: new DataLoader(keys => genStories(authToken, keys)),
  };
}
于 2016-02-08T13:35:47.067 に答える
2

このトピックで問題が発生した場合: dimadima の回答に基づいて、Relay/GraphQL/express 認証のサンプルリポジトリを作成しました。Express ミドルウェアと GraphQL Mutation を使用して、セッション データ (userId とロール) を Cookie に保存します。

于 2016-03-29T09:58:42.837 に答える