fireauth icon indicating copy to clipboard operation
fireauth copied to clipboard

Support Firebase 3 Authentication

Open theprojectabot opened this issue 8 years ago • 16 comments

When attempting an auth from a generated token by fire auth I am getting: FIRAuthErrorDomain Code=17000 "The custom token format is incorrect. Please check the documentation."

theprojectabot avatar Jun 27 '16 16:06 theprojectabot

http://stackoverflow.com/questions/37408684/is-it-still-possible-to-do-server-side-verification-of-tokens-in-firebase-3?noredirect=1#comment62518682_37408684

does fireauth not support firebase 3?

theprojectabot avatar Jun 27 '16 16:06 theprojectabot

It looks like it's not supported. I will not be able to get to this for a couple of hours but it will be top of my todo list.

In the meantime, if you know what the issue is and/or would like to open a PR I can review it and merge it quicker.

zabawaba99 avatar Jun 27 '16 16:06 zabawaba99

looks like the library needs to be changed to support a private key from a Firebase spawned service JSON file

https://firebase.google.com/docs/auth/server#use_a_jwt_library

Then the fireauth library could use that private key, mint a JWT token and then I can send to my client (iOS in this case)

theprojectabot avatar Jun 27 '16 16:06 theprojectabot

I tried this:

...
now := time.Now().Unix()
    jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{

        "iss": serviceAccountEmail,
        "sub": serviceAccountEmail,
        "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
        "iat": now,
        "exp": now + (60 * 60), // Maximum expiration time is one hour
        "uid": u.UID,
        /*
           "claims" : array(
               "premium_account" : true
           )*/

    })

    // Sign and get the complete encoded token as a string using the secret
    token, err = jwtToken.SignedString(privateKey)

    fmt.Println(token, err)

...

Im using the jwt-go library

but I get 'key invalid' when I send in my privateKey that I received when I got a service account key from google developer portal.

theprojectabot avatar Jun 27 '16 18:06 theprojectabot

https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac

theprojectabot avatar Jun 27 '16 18:06 theprojectabot

I'm in the firebase-community slack if you want to have a bit more of a back and forth on it. I'm looking at the examples you posted now.

zabawaba99 avatar Jun 27 '16 18:06 zabawaba99

I used the snippet that you gave and got a different error

2016/06/27 14:33:11 test did not pass {
  "error" : "Missing claim 'kid' in auth header."
}
exit status 1

What I did have to do was format and replaced the \n with actual spaces. Something like

secret := []byte(`-----BEGIN PRIVATE KEY-----
helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld
helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld
helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld
-----END PRIVATE KEY-----
`)

zabawaba99 avatar Jun 27 '16 18:06 zabawaba99

I got it to work!

signKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey)
    if err != nil {
        log.Print(err)
    }

    token, err = jwtToken.SignedString(signKey)

had to make sure that the key I got from google services was then parsed into the interface that jwt-go wanted then I got back a signed string that works with my iOS auth call.

theprojectabot avatar Jun 27 '16 18:06 theprojectabot

Also found a similar issue on a ruby client as well https://github.com/CodementorIO/rest-firebase/issues/12

zabawaba99 avatar Jun 28 '16 02:06 zabawaba99

@zabawaba99 I didnt need to include the 'kid' in my code. Though I did comment out the claims array in the payload I handed to jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{

theprojectabot avatar Jun 28 '16 15:06 theprojectabot

@theprojectabot Was the kid part of the auth header or the MapClaims?

zabawaba99 avatar Jun 28 '16 17:06 zabawaba99

I'm having trouble replicating what @theprojectabot did. Here is my current code:

jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
	"uid": "1",
})

signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(`I don't know what to put here, I tried a number of things including randomly generated private RSA keys`))
if err != nil {
	log.Fatal(err)
}

token, err := jwtToken.SignedString(signKey)
if err != nil {
	log.Fatal(err)
}

tmpl, err := template.ParseFiles("index.html")
if err != nil {
	log.Fatal(err)
}

if err := tmpl.Execute(w, token); err != nil {
	log.Fatal(err)
}

I recive the error message

Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key

when trying to run this code.

I am trying to authenticate Steam OAuth with Firebase but the internet has absolutely nothing for it.

tqwewe avatar Dec 29 '16 07:12 tqwewe

@Acidic9 following the instructions here on how to generate a private key for your application. You will be prompted to download a JSON file as your final step. This JSON file contains the private key you would put jwt.ParseRSAPrivateKeyFromPEM([]byte('here'))

zabawaba99 avatar Dec 29 '16 14:12 zabawaba99

I downloaded the JSON file, extracted the PrivateKey but when trying to use it with jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey)) It outputs 'key is invalid'.

I tried using strings.Replace(fbInfo.PrivateKey, `\n`, ` `, -1) as the private key (replacing \n with spaces) but it still output's the same error.

Here is the code I am trying to run:

package main

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"log"
	"strings"
)

type firebaseInfo struct {
	AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
	AuthURI                 string `json:"auth_uri"`
	ClientEmail             string `json:"client_email"`
	ClientID                string `json:"client_id"`
	ClientX509CertURL       string `json:"client_x509_cert_url"`
	PrivateKey              string `json:"private_key"`
	PrivateKeyID            string `json:"private_key_id"`
	ProjectID               string `json:"project_id"`
	TokenURI                string `json:"token_uri"`
	Type                    string `json:"type"`
}

func main() {
	// Read Firebase json file.
	fbInfoContent, err := ioutil.ReadFile("firebase-json.json")
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal json into fbInfo.
	var fbInfo firebaseInfo
	if err = json.Unmarshal(fbInfoContent, &fbInfo); err != nil {
		log.Fatal(err)
	}

	// Format privateKey.
	privateKey := strings.Replace(fbInfo.PrivateKey, `\n`, ` `, -1)

	// Create jwtToken with SigningMethodHS256.
	jwtToken := jwt.New(jwt.SigningMethodHS256)

	// Create signKey by parsing RSA private key.
	signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
	if err != nil {
		log.Fatal(err)
	}

	// Generate token with signKey.
	token, err := jwtToken.SignedString(signKey)
	if err != nil {
		log.Fatal(err)
	}

	// Finally, after 100000 google searches my problem may be solved.
	// Thank you heaps for helping me! :)
	fmt.Println("SUCCESS:", token)
}

Output: key is invalid

tqwewe avatar Dec 29 '16 16:12 tqwewe

@Acidic9 try this

package main

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"log"
	"time"
)

type firebaseInfo struct {
	AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
	AuthURI                 string `json:"auth_uri"`
	ClientEmail             string `json:"client_email"`
	ClientID                string `json:"client_id"`
	ClientX509CertURL       string `json:"client_x509_cert_url"`
	PrivateKey              string `json:"private_key"`
	PrivateKeyID            string `json:"private_key_id"`
	ProjectID               string `json:"project_id"`
	TokenURI                string `json:"token_uri"`
	Type                    string `json:"type"`
}

func main() {
	// Read Firebase json file.
	fbInfoContent, err := ioutil.ReadFile("firebase-json.json")
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal json into fbInfo.
	var fbInfo firebaseInfo
	if err = json.Unmarshal(fbInfoContent, &fbInfo); err != nil {
		log.Fatal(err)
	}

	// Create jwtToken with SigningMethodHS256.
	now := time.Now().Unix()
	jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
		"iss": fbInfo.ClientEmail,
		"sub": fbInfo.ClientEmail,
		"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
		"iat": now,
		"exp": now + (60 * 60), // Maximum expiration time is one hour
		"uid": "Some identifier for the user",
	})

	// Create signKey by parsing RSA private key.
	signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(fbInfo.PrivateKey))
	if err != nil {
		log.Fatal("error parsing rsa", err)
	}

	// Generate token with signKey.
	token, err := jwtToken.SignedString(signKey)
	if err != nil {
		log.Fatal("error signing string", err)
	}

	// Finally, after 100000 google searches my problem may be solved.
	// Thank you heaps for helping me! :)
	fmt.Println("SUCCESS:", token)
}

I was able to get the snippet above working. A few notes:

  • A private key needs the new lines. By removing them, the key is invalid.
  • There are specific claims you need to pass into your token in order to create a valid one. Without it Firebase will accept your token.

zabawaba99 avatar Dec 29 '16 19:12 zabawaba99

Thank you so so much man, seriously, I was trying to find a solution for so long and finally it works. I really appreciate you fixing it up for me!

tqwewe avatar Dec 30 '16 05:12 tqwewe