1 Star 0 Fork 0

systechn/firebase-admin-go

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
auth.go 44.11 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package snippets
import (
"context"
"encoding/base64"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"time"
firebase "firebase.google.com/go"
"firebase.google.com/go/auth"
"firebase.google.com/go/auth/hash"
"google.golang.org/api/iterator"
)
// ==================================================================
// https://firebase.google.com/docs/auth/admin/create-custom-tokens
// ==================================================================
func createCustomToken(ctx context.Context, app *firebase.App) string {
// [START create_custom_token_golang]
client, err := app.Auth(context.Background())
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.CustomToken(ctx, "some-uid")
if err != nil {
log.Fatalf("error minting custom token: %v\n", err)
}
log.Printf("Got custom token: %v\n", token)
// [END create_custom_token_golang]
return token
}
func createCustomTokenWithClaims(ctx context.Context, app *firebase.App) string {
// [START create_custom_token_claims_golang]
client, err := app.Auth(context.Background())
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
claims := map[string]interface{}{
"premiumAccount": true,
}
token, err := client.CustomTokenWithClaims(ctx, "some-uid", claims)
if err != nil {
log.Fatalf("error minting custom token: %v\n", err)
}
log.Printf("Got custom token: %v\n", token)
// [END create_custom_token_claims_golang]
return token
}
// ==================================================================
// https://firebase.google.com/docs/auth/admin/verify-id-tokens
// ==================================================================
func verifyIDToken(ctx context.Context, app *firebase.App, idToken string) *auth.Token {
// [START verify_id_token_golang]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatalf("error verifying ID token: %v\n", err)
}
log.Printf("Verified ID token: %v\n", token)
// [END verify_id_token_golang]
return token
}
// ==================================================================
// https://firebase.google.com/docs/auth/admin/manage-sessions
// ==================================================================
func revokeRefreshTokens(ctx context.Context, app *firebase.App, uid string) {
// [START revoke_tokens_golang]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
if err := client.RevokeRefreshTokens(ctx, uid); err != nil {
log.Fatalf("error revoking tokens for user: %v, %v\n", uid, err)
}
// accessing the user's TokenValidAfter
u, err := client.GetUser(ctx, uid)
if err != nil {
log.Fatalf("error getting user %s: %v\n", uid, err)
}
timestamp := u.TokensValidAfterMillis / 1000
log.Printf("the refresh tokens were revoked at: %d (UTC seconds) ", timestamp)
// [END revoke_tokens_golang]
}
func verifyIDTokenAndCheckRevoked(ctx context.Context, app *firebase.App, idToken string) *auth.Token {
// [START verify_id_token_and_check_revoked_golang]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.VerifyIDTokenAndCheckRevoked(ctx, idToken)
if err != nil {
if err.Error() == "ID token has been revoked" {
// Token is revoked. Inform the user to reauthenticate or signOut() the user.
} else {
// Token is invalid
}
}
log.Printf("Verified ID token: %v\n", token)
// [END verify_id_token_and_check_revoked_golang]
return token
}
// ==================================================================
// https://firebase.google.com/docs/auth/admin/manage-users
// ==================================================================
func getUser(ctx context.Context, app *firebase.App) *auth.UserRecord {
uid := "some_string_uid"
// [START get_user_golang]
// Get an auth client from the firebase.App
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
u, err := client.GetUser(ctx, uid)
if err != nil {
log.Fatalf("error getting user %s: %v\n", uid, err)
}
log.Printf("Successfully fetched user data: %v\n", u)
// [END get_user_golang]
return u
}
func getUserByEmail(ctx context.Context, client *auth.Client) *auth.UserRecord {
email := "some@email.com"
// [START get_user_by_email_golang]
u, err := client.GetUserByEmail(ctx, email)
if err != nil {
log.Fatalf("error getting user by email %s: %v\n", email, err)
}
log.Printf("Successfully fetched user data: %v\n", u)
// [END get_user_by_email_golang]
return u
}
func getUserByPhone(ctx context.Context, client *auth.Client) *auth.UserRecord {
phone := "+13214567890"
// [START get_user_by_phone_golang]
u, err := client.GetUserByPhoneNumber(ctx, phone)
if err != nil {
log.Fatalf("error getting user by phone %s: %v\n", phone, err)
}
log.Printf("Successfully fetched user data: %v\n", u)
// [END get_user_by_phone_golang]
return u
}
func createUser(ctx context.Context, client *auth.Client) *auth.UserRecord {
// [START create_user_golang]
params := (&auth.UserToCreate{}).
Email("user@example.com").
EmailVerified(false).
PhoneNumber("+15555550100").
Password("secretPassword").
DisplayName("John Doe").
PhotoURL("http://www.example.com/12345678/photo.png").
Disabled(false)
u, err := client.CreateUser(ctx, params)
if err != nil {
log.Fatalf("error creating user: %v\n", err)
}
log.Printf("Successfully created user: %v\n", u)
// [END create_user_golang]
return u
}
func createUserWithUID(ctx context.Context, client *auth.Client) *auth.UserRecord {
uid := "something"
// [START create_user_with_uid_golang]
params := (&auth.UserToCreate{}).
UID(uid).
Email("user@example.com").
PhoneNumber("+15555550100")
u, err := client.CreateUser(ctx, params)
if err != nil {
log.Fatalf("error creating user: %v\n", err)
}
log.Printf("Successfully created user: %v\n", u)
// [END create_user_with_uid_golang]
return u
}
func updateUser(ctx context.Context, client *auth.Client) {
uid := "d"
// [START update_user_golang]
params := (&auth.UserToUpdate{}).
Email("user@example.com").
EmailVerified(true).
PhoneNumber("+15555550100").
Password("newPassword").
DisplayName("John Doe").
PhotoURL("http://www.example.com/12345678/photo.png").
Disabled(true)
u, err := client.UpdateUser(ctx, uid, params)
if err != nil {
log.Fatalf("error updating user: %v\n", err)
}
log.Printf("Successfully updated user: %v\n", u)
// [END update_user_golang]
}
func deleteUser(ctx context.Context, client *auth.Client) {
uid := "d"
// [START delete_user_golang]
err := client.DeleteUser(ctx, uid)
if err != nil {
log.Fatalf("error deleting user: %v\n", err)
}
log.Printf("Successfully deleted user: %s\n", uid)
// [END delete_user_golang]
}
func customClaimsSet(ctx context.Context, app *firebase.App) {
uid := "uid"
// [START set_custom_user_claims_golang]
// Get an auth client from the firebase.App
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
// Set admin privilege on the user corresponding to uid.
claims := map[string]interface{}{"admin": true}
err = client.SetCustomUserClaims(ctx, uid, claims)
if err != nil {
log.Fatalf("error setting custom claims %v\n", err)
}
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
// [END set_custom_user_claims_golang]
// erase all existing custom claims
}
func customClaimsVerify(ctx context.Context, client *auth.Client) {
idToken := "token"
// [START verify_custom_claims_golang]
// Verify the ID token first.
token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatal(err)
}
claims := token.Claims
if admin, ok := claims["admin"]; ok {
if admin.(bool) {
//Allow access to requested admin resource.
}
}
// [END verify_custom_claims_golang]
}
func customClaimsRead(ctx context.Context, client *auth.Client) {
uid := "uid"
// [START read_custom_user_claims_golang]
// Lookup the user associated with the specified uid.
user, err := client.GetUser(ctx, uid)
if err != nil {
log.Fatal(err)
}
// The claims can be accessed on the user record.
if admin, ok := user.CustomClaims["admin"]; ok {
if admin.(bool) {
log.Println(admin)
}
}
// [END read_custom_user_claims_golang]
}
func customClaimsScript(ctx context.Context, client *auth.Client) {
// [START set_custom_user_claims_script_golang]
user, err := client.GetUserByEmail(ctx, "user@admin.example.com")
if err != nil {
log.Fatal(err)
}
// Confirm user is verified
if user.EmailVerified {
// Add custom claims for additional privileges.
// This will be picked up by the user on token refresh or next sign in on new device.
err := client.SetCustomUserClaims(ctx, user.UID, map[string]interface{}{"admin": true})
if err != nil {
log.Fatalf("error setting custom claims %v\n", err)
}
}
// [END set_custom_user_claims_script_golang]
}
func customClaimsIncremental(ctx context.Context, client *auth.Client) {
// [START set_custom_user_claims_incremental_golang]
user, err := client.GetUserByEmail(ctx, "user@admin.example.com")
if err != nil {
log.Fatal(err)
}
// Add incremental custom claim without overwriting existing claims.
currentCustomClaims := user.CustomClaims
if currentCustomClaims == nil {
currentCustomClaims = map[string]interface{}{}
}
if _, found := currentCustomClaims["admin"]; found {
// Add level.
currentCustomClaims["accessLevel"] = 10
// Add custom claims for additional privileges.
err := client.SetCustomUserClaims(ctx, user.UID, currentCustomClaims)
if err != nil {
log.Fatalf("error setting custom claims %v\n", err)
}
}
// [END set_custom_user_claims_incremental_golang]
}
func listUsers(ctx context.Context, client *auth.Client) {
// [START list_all_users_golang]
// Note, behind the scenes, the Users() iterator will retrive 1000 Users at a time through the API
iter := client.Users(ctx, "")
for {
user, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error listing users: %s\n", err)
}
log.Printf("read user user: %v\n", user)
}
// Iterating by pages 100 users at a time.
// Note that using both the Next() function on an iterator and the NextPage()
// on a Pager wrapping that same iterator will result in an error.
pager := iterator.NewPager(client.Users(ctx, ""), 100, "")
for {
var users []*auth.ExportedUserRecord
nextPageToken, err := pager.NextPage(&users)
if err != nil {
log.Fatalf("paging error %v\n", err)
}
for _, u := range users {
log.Printf("read user user: %v\n", u)
}
if nextPageToken == "" {
break
}
}
// [END list_all_users_golang]
}
func importUsers(ctx context.Context, app *firebase.App) {
// [START build_user_list]
// Up to 1000 users can be imported at once.
var users []*auth.UserToImport
users = append(users, (&auth.UserToImport{}).
UID("uid1").
Email("user1@example.com").
PasswordHash([]byte("passwordHash1")).
PasswordSalt([]byte("salt1")))
users = append(users, (&auth.UserToImport{}).
UID("uid2").
Email("user2@example.com").
PasswordHash([]byte("passwordHash2")).
PasswordSalt([]byte("salt2")))
// [END build_user_list]
// [START import_users]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalln("Error initializing Auth client", err)
}
h := hash.HMACSHA256{
Key: []byte("secretKey"),
}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Unrecoverable error prevented the operation from running", err)
}
log.Printf("Successfully imported %d users\n", result.SuccessCount)
log.Printf("Failed to import %d users\n", result.FailureCount)
for _, e := range result.Errors {
log.Printf("Failed to import user at index: %d due to error: %s\n", e.Index, e.Reason)
}
// [END import_users]
}
func importWithHMAC(ctx context.Context, client *auth.Client) {
// [START import_with_hmac]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
Email("user@example.com").
PasswordHash([]byte("password-hash")).
PasswordSalt([]byte("salt")),
}
h := hash.HMACSHA256{
Key: []byte("secret"),
}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_hmac]
}
func importWithPBKDF(ctx context.Context, client *auth.Client) {
// [START import_with_pbkdf]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
Email("user@example.com").
PasswordHash([]byte("password-hash")).
PasswordSalt([]byte("salt")),
}
h := hash.PBKDF2SHA256{
Rounds: 100000,
}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_pbkdf]
}
func importWithStandardScrypt(ctx context.Context, client *auth.Client) {
// [START import_with_standard_scrypt]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
Email("user@example.com").
PasswordHash([]byte("password-hash")).
PasswordSalt([]byte("salt")),
}
h := hash.StandardScrypt{
MemoryCost: 1024,
Parallelization: 16,
BlockSize: 8,
DerivedKeyLength: 64,
}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_standard_scrypt]
}
func importWithBcrypt(ctx context.Context, client *auth.Client) {
// [START import_with_bcrypt]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
Email("user@example.com").
PasswordHash([]byte("password-hash")).
PasswordSalt([]byte("salt")),
}
h := hash.Bcrypt{}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_bcrypt]
}
func importWithScrypt(ctx context.Context, client *auth.Client) {
// [START import_with_scrypt]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
Email("user@example.com").
PasswordHash([]byte("password-hash")).
PasswordSalt([]byte("salt")),
}
b64decode := func(s string) []byte {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
log.Fatalln("Failed to decode string", err)
}
return b
}
// All the parameters below can be obtained from the Firebase Console's "Users"
// section. Base64 encoded parameters must be decoded into raw bytes.
h := hash.Scrypt{
Key: b64decode("base64-secret"),
SaltSeparator: b64decode("base64-salt-separator"),
Rounds: 8,
MemoryCost: 14,
}
result, err := client.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_scrypt]
}
func importWithoutPassword(ctx context.Context, client *auth.Client) {
// [START import_without_password]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
DisplayName("John Doe").
Email("johndoe@gmail.com").
PhotoURL("http://www.example.com/12345678/photo.png").
EmailVerified(true).
PhoneNumber("+11234567890").
CustomClaims(map[string]interface{}{"admin": true}). // set this user as admin
ProviderData([]*auth.UserProvider{ // user with Google provider
{
UID: "google-uid",
Email: "johndoe@gmail.com",
DisplayName: "John Doe",
PhotoURL: "http://www.example.com/12345678/photo.png",
ProviderID: "google.com",
},
}),
}
result, err := client.ImportUsers(ctx, users)
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_without_password]
}
func loginHandler(client *auth.Client) http.HandlerFunc {
// [START session_login]
return func(w http.ResponseWriter, r *http.Request) {
// Get the ID token sent by the client
defer r.Body.Close()
idToken, err := getIDTokenFromBody(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Set session expiration to 5 days.
expiresIn := time.Hour * 24 * 5
// Create the session cookie. This will also verify the ID token in the process.
// The session cookie will have the same claims as the ID token.
// To only allow session cookie setting on recent sign-in, auth_time in ID token
// can be checked to ensure user was recently signed in before creating a session cookie.
cookie, err := client.SessionCookie(r.Context(), idToken, expiresIn)
if err != nil {
http.Error(w, "Failed to create a session cookie", http.StatusInternalServerError)
return
}
// Set cookie policy for session cookie.
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: cookie,
MaxAge: int(expiresIn.Seconds()),
HttpOnly: true,
Secure: true,
})
w.Write([]byte(`{"status": "success"}`))
}
// [END session_login]
}
func loginWithAuthTimeCheckHandler(client *auth.Client) http.HandlerFunc {
// [START check_auth_time]
return func(w http.ResponseWriter, r *http.Request) {
// Get the ID token sent by the client
defer r.Body.Close()
idToken, err := getIDTokenFromBody(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
decoded, err := client.VerifyIDToken(r.Context(), idToken)
if err != nil {
http.Error(w, "Invalid ID token", http.StatusUnauthorized)
return
}
// Return error if the sign-in is older than 5 minutes.
if time.Now().Unix()-decoded.Claims["auth_time"].(int64) > 5*60 {
http.Error(w, "Recent sign-in required", http.StatusUnauthorized)
return
}
expiresIn := time.Hour * 24 * 5
cookie, err := client.SessionCookie(r.Context(), idToken, expiresIn)
if err != nil {
http.Error(w, "Failed to create a session cookie", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: cookie,
MaxAge: int(expiresIn.Seconds()),
HttpOnly: true,
Secure: true,
})
w.Write([]byte(`{"status": "success"}`))
}
// [END check_auth_time]
}
func userProfileHandler(client *auth.Client) http.HandlerFunc {
serveContentForUser := func(w http.ResponseWriter, r *http.Request, claims *auth.Token) {
log.Println("Serving content")
}
// [START session_verify]
return func(w http.ResponseWriter, r *http.Request) {
// Get the ID token sent by the client
cookie, err := r.Cookie("session")
if err != nil {
// Session cookie is unavailable. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
// Verify the session cookie. In this case an additional check is added to detect
// if the user's Firebase session was revoked, user deleted/disabled, etc.
decoded, err := client.VerifySessionCookieAndCheckRevoked(r.Context(), cookie.Value)
if err != nil {
// Session cookie is invalid. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
serveContentForUser(w, r, decoded)
}
// [END session_verify]
}
func adminUserHandler(client *auth.Client) http.HandlerFunc {
serveContentForAdmin := func(w http.ResponseWriter, r *http.Request, claims *auth.Token) {
log.Println("Serving content")
}
// [START session_verify_with_permission_check]
return func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session")
if err != nil {
// Session cookie is unavailable. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
decoded, err := client.VerifySessionCookieAndCheckRevoked(r.Context(), cookie.Value)
if err != nil {
// Session cookie is invalid. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
// Check custom claims to confirm user is an admin.
if decoded.Claims["admin"] != true {
http.Error(w, "Insufficient permissions", http.StatusUnauthorized)
return
}
serveContentForAdmin(w, r, decoded)
}
// [END session_verify_with_permission_check]
}
func sessionLogoutHandler() http.HandlerFunc {
// [START session_clear]
return func(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: "",
MaxAge: 0,
})
http.Redirect(w, r, "/login", http.StatusFound)
}
// [END session_clear]
}
func sessionLogoutHandlerWithRevocation(client *auth.Client) http.HandlerFunc {
// [START session_clear_and_revoke]
return func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session")
if err != nil {
// Session cookie is unavailable. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
decoded, err := client.VerifySessionCookie(r.Context(), cookie.Value)
if err != nil {
// Session cookie is invalid. Force user to login.
http.Redirect(w, r, "/login", http.StatusFound)
return
}
if err := client.RevokeRefreshTokens(r.Context(), decoded.UID); err != nil {
http.Error(w, "Failed to revoke refresh token", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: "",
MaxAge: 0,
})
http.Redirect(w, r, "/login", http.StatusFound)
}
// [END session_clear_and_revoke]
}
func getIDTokenFromBody(r *http.Request) (string, error) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
return "", err
}
var parsedBody struct {
IDToken string `json:"idToken"`
}
err = json.Unmarshal(b, &parsedBody)
return parsedBody.IDToken, err
}
func newActionCodeSettings() *auth.ActionCodeSettings {
// [START init_action_code_settings]
actionCodeSettings := &auth.ActionCodeSettings{
URL: "https://www.example.com/checkout?cartId=1234",
HandleCodeInApp: true,
IOSBundleID: "com.example.ios",
AndroidPackageName: "com.example.android",
AndroidInstallApp: true,
AndroidMinimumVersion: "12",
DynamicLinkDomain: "coolapp.page.link",
}
// [END init_action_code_settings]
return actionCodeSettings
}
func generatePasswordResetLink(ctx context.Context, client *auth.Client) {
actionCodeSettings := newActionCodeSettings()
displayName := "Example User"
// [START password_reset_link]
email := "user@example.com"
link, err := client.PasswordResetLinkWithSettings(ctx, email, actionCodeSettings)
if err != nil {
log.Fatalf("error generating email link: %v\n", err)
}
// Construct password reset template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link)
// [END password_reset_link]
}
func generateEmailVerificationLink(ctx context.Context, client *auth.Client) {
actionCodeSettings := newActionCodeSettings()
displayName := "Example User"
// [START email_verification_link]
email := "user@example.com"
link, err := client.EmailVerificationLinkWithSettings(ctx, email, actionCodeSettings)
if err != nil {
log.Fatalf("error generating email link: %v\n", err)
}
// Construct email verification template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link)
// [END email_verification_link]
}
func generateEmailSignInLink(ctx context.Context, client *auth.Client) {
actionCodeSettings := newActionCodeSettings()
displayName := "Example User"
// [START sign_in_with_email_link]
email := "user@example.com"
link, err := client.EmailSignInLink(ctx, email, actionCodeSettings)
if err != nil {
log.Fatalf("error generating email link: %v\n", err)
}
// Construct sign-in with email link template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link)
// [END sign_in_with_email_link]
}
// Place holder function to make the compiler happy. This is referenced by all email action
// link snippets.
func sendCustomEmail(email, displayName, link string) {}
// =====================================================================================
// https://cloud.google.com/identity-platform/docs/managing-providers-programmatically
// =====================================================================================
func createSAMLProviderConfig(ctx context.Context, client *auth.Client) {
// [START create_saml_provider]
newConfig := (&auth.SAMLProviderConfigToCreate{}).
DisplayName("SAML provider name").
Enabled(true).
ID("saml.myProvider").
IDPEntityID("IDP_ENTITY_ID").
SSOURL("https://example.com/saml/sso/1234/").
X509Certificates([]string{
"-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----",
"-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----",
}).
RPEntityID("RP_ENTITY_ID").
CallbackURL("https://project-id.firebaseapp.com/__/auth/handler")
saml, err := client.CreateSAMLProviderConfig(ctx, newConfig)
if err != nil {
log.Fatalf("error creating SAML provider: %v\n", err)
}
log.Printf("Created new SAML provider: %s", saml.ID)
// [END create_saml_provider]
}
func updateSAMLProviderConfig(ctx context.Context, client *auth.Client) {
// [START update_saml_provider]
updatedConfig := (&auth.SAMLProviderConfigToUpdate{}).
X509Certificates([]string{
"-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----",
"-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----",
})
saml, err := client.UpdateSAMLProviderConfig(ctx, "saml.myProvider", updatedConfig)
if err != nil {
log.Fatalf("error updating SAML provider: %v\n", err)
}
log.Printf("Updated SAML provider: %s", saml.ID)
// [END update_saml_provider]
}
func getSAMLProviderConfig(ctx context.Context, client *auth.Client) {
// [START get_saml_provider]
saml, err := client.SAMLProviderConfig(ctx, "saml.myProvider")
if err != nil {
log.Fatalf("error retrieving SAML provider: %v\n", err)
}
log.Printf("%s %t", saml.DisplayName, saml.Enabled)
// [END get_saml_provider]
}
func deleteSAMLProviderConfig(ctx context.Context, client *auth.Client) {
// [START delete_saml_provider]
if err := client.DeleteSAMLProviderConfig(ctx, "saml.myProvider"); err != nil {
log.Fatalf("error deleting SAML provider: %v\n", err)
}
// [END delete_saml_provider]
}
func listSAMLProviderConfigs(ctx context.Context, client *auth.Client) {
// [START list_saml_providers]
iter := client.SAMLProviderConfigs(ctx, "nextPageToken")
for {
saml, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error retrieving SAML providers: %v\n", err)
}
log.Printf("%s\n", saml.ID)
}
// [END list_saml_providers]
}
func createOIDCProviderConfig(ctx context.Context, client *auth.Client) {
// [START create_oidc_provider]
newConfig := (&auth.OIDCProviderConfigToCreate{}).
DisplayName("OIDC provider name").
Enabled(true).
ID("oidc.myProvider").
ClientID("CLIENT_ID2").
Issuer("https://oidc.com/CLIENT_ID2")
oidc, err := client.CreateOIDCProviderConfig(ctx, newConfig)
if err != nil {
log.Fatalf("error creating OIDC provider: %v\n", err)
}
log.Printf("Created new OIDC provider: %s", oidc.ID)
// [END create_oidc_provider]
}
func updateOIDCProviderConfig(ctx context.Context, client *auth.Client) {
// [START update_oidc_provider]
updatedConfig := (&auth.OIDCProviderConfigToUpdate{}).
DisplayName("OIDC provider name").
Enabled(true).
ClientID("CLIENT_ID").
Issuer("https://oidc.com")
oidc, err := client.UpdateOIDCProviderConfig(ctx, "oidc.myProvider", updatedConfig)
if err != nil {
log.Fatalf("error updating OIDC provider: %v\n", err)
}
log.Printf("Updated OIDC provider: %s", oidc.ID)
// [END update_oidc_provider]
}
func getOIDCProviderConfig(ctx context.Context, client *auth.Client) {
// [START get_oidc_provider]
oidc, err := client.OIDCProviderConfig(ctx, "oidc.myProvider")
if err != nil {
log.Fatalf("error retrieving OIDC provider: %v\n", err)
}
log.Printf("%s %t", oidc.DisplayName, oidc.Enabled)
// [END get_oidc_provider]
}
func deleteOIDCProviderConfig(ctx context.Context, client *auth.Client) {
// [START delete_oidc_provider]
if err := client.DeleteOIDCProviderConfig(ctx, "oidc.myProvider"); err != nil {
log.Fatalf("error deleting OIDC provider: %v\n", err)
}
// [END delete_oidc_provider]
}
func listOIDCProviderConfigs(ctx context.Context, client *auth.Client) {
// [START list_oidc_providers]
iter := client.OIDCProviderConfigs(ctx, "nextPageToken")
for {
oidc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error retrieving OIDC providers: %v\n", err)
}
log.Printf("%s\n", oidc.ID)
}
// [END list_oidc_providers]
}
// ================================================================================
// https://cloud.google.com/identity-platform/docs/multi-tenancy-managing-tenants
// =================================================================================
func getTenantClient(ctx context.Context, app *firebase.App, tenantID string) *auth.TenantClient {
// [START get_tenant_client]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error initializing auth client: %v\n", err)
}
tm := client.TenantManager
tenantClient, err := tm.AuthForTenant(tenantID)
if err != nil {
log.Fatalf("error initializing tenant-aware auth client: %v\n", err)
}
// [END get_tenant_client]
return tenantClient
}
func getTenant(ctx context.Context, client *auth.Client, tenantID string) {
// [START get_tenant]
tenant, err := client.TenantManager.Tenant(ctx, tenantID)
if err != nil {
log.Fatalf("error retrieving tenant: %v\n", err)
}
log.Printf("Retreieved tenant: %q\n", tenant.ID)
// [END get_tenant]
}
func createTenant(ctx context.Context, client *auth.Client) {
// [START create_tenant]
config := (&auth.TenantToCreate{}).
DisplayName("myTenant1").
EnableEmailLinkSignIn(true).
AllowPasswordSignUp(true)
tenant, err := client.TenantManager.CreateTenant(ctx, config)
if err != nil {
log.Fatalf("error creating tenant: %v\n", err)
}
log.Printf("Created tenant: %q\n", tenant.ID)
// [END create_tenant]
}
func updateTenant(ctx context.Context, client *auth.Client, tenantID string) {
// [START update_tenant]
config := (&auth.TenantToUpdate{}).
DisplayName("updatedName").
AllowPasswordSignUp(false) // Disable email provider
tenant, err := client.TenantManager.UpdateTenant(ctx, tenantID, config)
if err != nil {
log.Fatalf("error updating tenant: %v\n", err)
}
log.Printf("Updated tenant: %q\n", tenant.ID)
// [END update_tenant]
}
func deleteTenant(ctx context.Context, client *auth.Client, tenantID string) {
// [START delete_tenant]
if err := client.TenantManager.DeleteTenant(ctx, tenantID); err != nil {
log.Fatalf("error deleting tenant: %v\n", tenantID)
}
// [END delete_tenant]
}
func listTenants(ctx context.Context, client *auth.Client) {
// [START list_tenants]
iter := client.TenantManager.Tenants(ctx, "")
for {
tenant, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error listing tenants: %v\n", err)
}
log.Printf("Retrieved tenant: %q\n", tenant.ID)
}
// [END list_tenants]
}
func createProviderTenant(ctx context.Context, client *auth.Client) {
// [START get_tenant_client_short]
tenantClient, err := client.TenantManager.AuthForTenant("TENANT-ID")
if err != nil {
log.Fatalf("error initializing tenant client: %v\n", err)
}
// [END get_tenant_client_short]
// [START create_saml_provider_tenant]
newConfig := (&auth.SAMLProviderConfigToCreate{}).
DisplayName("SAML provider name").
Enabled(true).
ID("saml.myProvider").
IDPEntityID("IDP_ENTITY_ID").
SSOURL("https://example.com/saml/sso/1234/").
X509Certificates([]string{
"-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----",
"-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----",
}).
RPEntityID("RP_ENTITY_ID").
// Using the default callback URL.
CallbackURL("https://project-id.firebaseapp.com/__/auth/handler")
saml, err := tenantClient.CreateSAMLProviderConfig(ctx, newConfig)
if err != nil {
log.Fatalf("error creating SAML provider: %v\n", err)
}
log.Printf("Created new SAML provider: %s", saml.ID)
// [END create_saml_provider_tenant]
}
func updateProviderTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START update_saml_provider_tenant]
updatedConfig := (&auth.SAMLProviderConfigToUpdate{}).
X509Certificates([]string{
"-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----",
"-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----",
})
saml, err := tenantClient.UpdateSAMLProviderConfig(ctx, "saml.myProvider", updatedConfig)
if err != nil {
log.Fatalf("error updating SAML provider: %v\n", err)
}
log.Printf("Updated SAML provider: %s", saml.ID)
// [END update_saml_provider_tenant]
}
func getProviderTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START get_saml_provider_tenant]
saml, err := tenantClient.SAMLProviderConfig(ctx, "saml.myProvider")
if err != nil {
log.Fatalf("error retrieving SAML provider: %v\n", err)
}
// Get display name and whether it is enabled.
log.Printf("%s %t", saml.DisplayName, saml.Enabled)
// [END get_saml_provider_tenant]
}
func listProviderConfigsTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START list_saml_providers_tenant]
iter := tenantClient.SAMLProviderConfigs(ctx, "nextPageToken")
for {
saml, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error retrieving SAML providers: %v\n", err)
}
log.Printf("%s\n", saml.ID)
}
// [END list_saml_providers_tenant]
}
func deleteProviderConfigTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START delete_saml_provider_tenant]
if err := tenantClient.DeleteSAMLProviderConfig(ctx, "saml.myProvider"); err != nil {
log.Fatalf("error deleting SAML provider: %v\n", err)
}
// [END delete_saml_provider_tenant]
}
func getUserTenant(ctx context.Context, tenantClient *auth.TenantClient) *auth.UserRecord {
uid := "some_string_uid"
// [START get_user_tenant]
// Get an auth client from the firebase.App
u, err := tenantClient.GetUser(ctx, uid)
if err != nil {
log.Fatalf("error getting user %s: %v\n", uid, err)
}
log.Printf("Successfully fetched user data: %v\n", u)
// [END get_user_tenant]
return u
}
func getUserByEmailTenant(ctx context.Context, tenantClient *auth.TenantClient) *auth.UserRecord {
email := "some@email.com"
// [START get_user_by_email_tenant]
u, err := tenantClient.GetUserByEmail(ctx, email)
if err != nil {
log.Fatalf("error getting user by email %s: %v\n", email, err)
}
log.Printf("Successfully fetched user data: %v\n", u)
// [END get_user_by_email_tenant]
return u
}
func createUserTenant(ctx context.Context, tenantClient *auth.TenantClient) *auth.UserRecord {
// [START create_user_tenant]
params := (&auth.UserToCreate{}).
Email("user@example.com").
EmailVerified(false).
PhoneNumber("+15555550100").
Password("secretPassword").
DisplayName("John Doe").
PhotoURL("http://www.example.com/12345678/photo.png").
Disabled(false)
u, err := tenantClient.CreateUser(ctx, params)
if err != nil {
log.Fatalf("error creating user: %v\n", err)
}
log.Printf("Successfully created user: %v\n", u)
// [END create_user_tenant]
return u
}
func updateUserTenant(ctx context.Context, tenantClient *auth.TenantClient, uid string) {
// [START update_user_tenant]
params := (&auth.UserToUpdate{}).
Email("user@example.com").
EmailVerified(true).
PhoneNumber("+15555550100").
Password("newPassword").
DisplayName("John Doe").
PhotoURL("http://www.example.com/12345678/photo.png").
Disabled(true)
u, err := tenantClient.UpdateUser(ctx, uid, params)
if err != nil {
log.Fatalf("error updating user: %v\n", err)
}
log.Printf("Successfully updated user: %v\n", u)
// [END update_user_tenant]
}
func deleteUserTenant(ctx context.Context, tenantClient *auth.TenantClient, uid string) {
// [START delete_user_tenant]
if err := tenantClient.DeleteUser(ctx, uid); err != nil {
log.Fatalf("error deleting user: %v\n", err)
}
log.Printf("Successfully deleted user: %s\n", uid)
// [END delete_user_tenant]
}
func listUsersTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START list_all_users_tenant]
// Note, behind the scenes, the Users() iterator will retrive 1000 Users at a time through the API
iter := tenantClient.Users(ctx, "")
for {
user, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error listing users: %s\n", err)
}
log.Printf("read user user: %v\n", user)
}
// Iterating by pages 100 users at a time.
// Note that using both the Next() function on an iterator and the NextPage()
// on a Pager wrapping that same iterator will result in an error.
pager := iterator.NewPager(tenantClient.Users(ctx, ""), 100, "")
for {
var users []*auth.ExportedUserRecord
nextPageToken, err := pager.NextPage(&users)
if err != nil {
log.Fatalf("paging error %v\n", err)
}
for _, u := range users {
log.Printf("read user user: %v\n", u)
}
if nextPageToken == "" {
break
}
}
// [END list_all_users_tenant]
}
func importWithHMACTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START import_with_hmac_tenant]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("uid1").
Email("user1@example.com").
PasswordHash([]byte("password-hash-1")).
PasswordSalt([]byte("salt1")),
(&auth.UserToImport{}).
UID("uid2").
Email("user2@example.com").
PasswordHash([]byte("password-hash-2")).
PasswordSalt([]byte("salt2")),
}
h := hash.HMACSHA256{
Key: []byte("secret"),
}
result, err := tenantClient.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_with_hmac_tenant]
}
func importWithoutPasswordTenant(ctx context.Context, tenantClient *auth.TenantClient) {
// [START import_without_password_tenant]
users := []*auth.UserToImport{
(&auth.UserToImport{}).
UID("some-uid").
DisplayName("John Doe").
Email("johndoe@acme.com").
PhotoURL("https://www.example.com/12345678/photo.png").
EmailVerified(true).
PhoneNumber("+11234567890").
// Set this user as admin.
CustomClaims(map[string]interface{}{"admin": true}).
// User with SAML provider.
ProviderData([]*auth.UserProvider{
{
UID: "saml-uid",
Email: "johndoe@acme.com",
DisplayName: "John Doe",
PhotoURL: "https://www.example.com/12345678/photo.png",
ProviderID: "saml.acme",
},
}),
}
result, err := tenantClient.ImportUsers(ctx, users)
if err != nil {
log.Fatalln("Error importing users", err)
}
for _, e := range result.Errors {
log.Println("Failed to import user", e.Reason)
}
// [END import_without_password_tenant]
}
func verifyIDTokenTenant(ctx context.Context, tenantClient *auth.TenantClient, idToken string) {
// [START verify_id_token_tenant]
// idToken comes from the client app
token, err := tenantClient.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatalf("error verifying ID token: %v\n", err)
}
// This should be set to TENANT-ID. Otherwise auth/mismatching-tenant-id error thrown.
log.Printf("Verified ID token from tenant: %q\n", token.Firebase.Tenant)
// [END verify_id_token_tenant]
}
func verifyIDTokenAccessControlTenant(ctx context.Context, tenantClient *auth.TenantClient, idToken string) {
// [START id_token_access_control_tenant]
token, err := tenantClient.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatalf("error verifying ID token: %v\n", err)
}
if token.Firebase.Tenant == "TENANT-ID1" {
// Allow appropriate level of access for TENANT-ID1.
} else if token.Firebase.Tenant == "TENANT-ID2" {
// Allow appropriate level of access for TENANT-ID2.
} else {
// Access not allowed -- Handle error
}
// [END id_token_access_control_tenant]
}
func revokeRefreshTokensTenant(ctx context.Context, tenantClient *auth.TenantClient, uid string) {
// [START revoke_tokens_tenant]
// Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
if err := tenantClient.RevokeRefreshTokens(ctx, uid); err != nil {
log.Fatalf("error revoking tokens for user: %v, %v\n", uid, err)
}
// accessing the user's TokenValidAfter
u, err := tenantClient.GetUser(ctx, uid)
if err != nil {
log.Fatalf("error getting user %s: %v\n", uid, err)
}
timestamp := u.TokensValidAfterMillis / 1000
log.Printf("the refresh tokens were revoked at: %d (UTC seconds) ", timestamp)
// [END revoke_tokens_tenant]
}
func verifyIDTokenAndCheckRevokedTenant(ctx context.Context, tenantClient *auth.TenantClient, idToken string) {
// [START verify_id_token_and_check_revoked_tenant]
// Verify the ID token for a specific tenant while checking if the token is revoked.
token, err := tenantClient.VerifyIDTokenAndCheckRevoked(ctx, idToken)
if err != nil {
if auth.IsIDTokenRevoked(err) {
// Token is revoked. Inform the user to reauthenticate or signOut() the user.
} else {
// Token is invalid
}
}
log.Printf("Verified ID token: %v\n", token)
// [END verify_id_token_and_check_revoked_tenant]
}
func customClaimsSetTenant(ctx context.Context, tenantClient *auth.TenantClient, uid string) {
// [START set_custom_user_claims_tenant]
// Set admin privilege on the user corresponding to uid.
claims := map[string]interface{}{"admin": true}
if err := tenantClient.SetCustomUserClaims(ctx, uid, claims); err != nil {
log.Fatalf("error setting custom claims %v\n", err)
}
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
// [END set_custom_user_claims_tenant]
}
func customClaimsVerifyTenant(ctx context.Context, tenantClient *auth.TenantClient, idToken string) {
// [START verify_custom_claims_tenant]
// Verify the ID token first.
token, err := tenantClient.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatal(err)
}
claims := token.Claims
if admin, ok := claims["admin"]; ok {
if admin.(bool) {
//Allow access to requested admin resource.
}
}
// [END verify_custom_claims_tenant]
}
func customClaimsReadTenant(ctx context.Context, tenantClient *auth.TenantClient, uid string) {
// [START read_custom_user_claims_tenant]
// Lookup the user associated with the specified uid.
user, err := tenantClient.GetUser(ctx, uid)
if err != nil {
log.Fatal(err)
}
// The claims can be accessed on the user record.
if admin, ok := user.CustomClaims["admin"]; ok {
if admin.(bool) {
log.Println(admin)
}
}
// [END read_custom_user_claims_tenant]
}
func generateEmailVerificationLinkTenant(ctx context.Context, tenantClient *auth.TenantClient) {
displayName := "Example User"
email := "user@example.com"
// [START email_verification_link_tenant]
actionCodeSettings := &auth.ActionCodeSettings{
// URL you want to redirect back to. The domain (www.example.com) for
// this URL must be whitelisted in the GCP Console.
URL: "https://www.example.com/checkout?cartId=1234",
// This must be true for email link sign-in.
HandleCodeInApp: true,
IOSBundleID: "com.example.ios",
AndroidPackageName: "com.example.android",
AndroidInstallApp: true,
AndroidMinimumVersion: "12",
// FDL custom domain.
DynamicLinkDomain: "coolapp.page.link",
}
link, err := tenantClient.EmailVerificationLinkWithSettings(ctx, email, actionCodeSettings)
if err != nil {
log.Fatalf("error generating email link: %v\n", err)
}
// Construct email verification template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link)
// [END email_verification_link_tenant]
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/systechn/firebase-admin-go.git
git@gitee.com:systechn/firebase-admin-go.git
systechn
firebase-admin-go
firebase-admin-go
v3.11.1

搜索帮助

0d507c66 1850385 C8b1a773 1850385