Edited at

Firebase HTTPS callable function を試してみる

More than 1 year has passed since last update.

2018年3月20日の Firebase の更新で、クライアントから直接 Cloud Functions が叩ける機能が入りました。

Call Functions from Your App  |  Firebase

認証状態を持ったまま Cloud Functions を叩くことができるので、今までのように authenticated-json-apiauthorized-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 行程度なので読んでみると良いと思います。