2018年3月20日の Firebase の更新で、クライアントから直接 Cloud Functions が叩ける機能が入りました。
Call Functions from Your App | Firebase
認証状態を持ったまま Cloud Functions を叩くことができるので、今までのように authenticated-json-api や authorized-https-endpoint のような自前の認証は不要になりました。
使ってみる
iOS + Cloud Functions で使ってみました。
Cloud Functions
TypeScript の例です。
export const httpcallable = functions.https.onCall((data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.')
}
if (!data.name) {
console.log('data.name is not found')
throw new functions.https.HttpsError('invalid-argument', 'data.name is undefined.', data)
}
return {
uid: context.auth!.uid,
name: data.name,
data: data,
context: context
}
})
Cloud Functions は data, context を受け取ることができます。
data はクライアント側から渡すことのできるパラメータです。
context には認証情報などが含まれています、 context の型をみてみましょう。
export interface CallableContext {
auth?: {
uid: string;
token: firebase.auth.DecodedIdToken;
};
instanceIdToken?: string;
}
認証済みだったら auth に値が入ってきます。auth があるか、anonymous かどうかもその内容で判定できます。
実際のパラメータはこのようになっていました。
未ログイン
context { instanceIdToken: 'hogehoge-token' }
anonymous ログイン
context { auth:
{ uid: 'uid',
token:
{ iss: 'https://securetoken.google.com/myproj',
provider_id: 'anonymous',
aud: 'myproj',
auth_time: 1521769673,
user_id: 'uid',
sub: 'uid',
iat: 1521782508,
exp: 1521786108,
firebase: [Object],
uid: 'uid' } },
instanceIdToken: 'hogehoge-token' }
firebase のなかみ
firebase { identities: {}, sign_in_provider: 'anonymous' }
facebook ログイン
context { auth:
{ uid: 'uid',
token:
{ iss: 'https://securetoken.google.com/myproj',
name: 'Mike',
picture: 'https://scontent.xx.fbcdn.net/v/jpg',
aud: 'myproj',
auth_time: 1521788688,
user_id: 'uid',
sub: 'uid',
iat: 1521788688,
exp: 1521792288,
email: 'hoge@tfbnw.net',
email_verified: false,
firebase: [Object],
uid: 'uid' } },
instanceIdToken: 'hogehoge-token' }
firebase の中はこんな感じ
firebase { identities:
{ 'facebook.com': [ '100258...' ],
email: [ 'hoge@tfbnw.net' ] },
sign_in_provider: 'facebook.com' }
注意
エラーを返す時は throw しましょう。 return new functions.https.HttpsError('invalid-argument', 'data.name is undefined.', data)
だとクライアント側に無が返されます。 (result も error も nil が返る)
エラー
下記のエラー型が定義されています。HTTP Status Code のように使いやすく良いですね。
export declare type FunctionsErrorCode =
'ok' | 'cancelled' | 'unknown' | 'invalid-argument' |
'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' |
'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' |
'data-loss' | 'unauthenticated';
iOS
これだけで認証済みで Functions を叩くことができます。便利ですね。
import FirebaseFunctions
functions.httpsCallable("httpcallable").call(["name": "taro"]) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
let code = FIRFunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
}
}
if let name = (result?.data as? [String: Any])?["name"] as? String {
print(name)
}
}
response は result or error になっています。
result.data に Functions が return したデータが入ってきます。
result.context に auth 情報も入っていました。
内部実装はどうなっているか
firebase-functions の内部実装を読んでみると、HTTP で叩いているだけのようです。
ざっくり読んだだけですが、 authorized-https-endpoint をラップしたメソッドを提供してくれているだけのようです、onCall のコードは 50 行程度なので読んでみると良いと思います。