2016-07-04 35 views
10

Sto cercando di implementare il meccanismo di autenticazione Firebase 3 utilizzando i token personalizzati (come descritto in https: // firebase.google.com/docs/auth/server/create -Custom-gettoni).Firebase 3: creazione di un token di autenticazione personalizzato utilizzando .net e C#

Il mio server è un'applicazione MVC ASP.NET.

Quindi in base alle istruzioni (https://firebase.google.com/docs/server/setup) ho creato un account di servizio per la mia applicazione Firebase e generato una chiave nel formato '.p12'.

Dopo quello secondo le istruzioni qui (https://firebase.google.com/docs/auth/server/create-custom-tokens#create_custom_tokens_using_a_third-party_jwt_library) ho provato a generare un token personalizzato e firmarlo utilizzando la chiave ricevuta nel passaggio precedente. Per la generazione di token ho usato biblioteca SystemIdentityModel.Tokens.Jwt da Microsoft, in modo che il codice è simile al seguente:

var now = DateTime.UtcNow; 
var tokenHandler = new JwtSecurityTokenHandler(); 
var key = new X509AsymmetricSecurityKey(new X509Certificate2(p12path, p12pwd)); 
var signinCredentials = new SigningCredentials(key, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmlenc#rsa-sha256"); 
Int32 nowInUnixTimestamp = (Int32)(now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; 

var token = tokenHandler.CreateToken(
      issuer: serviceAccountEmail, 
      audience: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",     
      signingCredentials: signinCredentials, 
      subject: new ClaimsIdentity(new Claim[] 
        { 
        new Claim("sub", serviceAccountEmail), 
        new Claim("iat", nowInUnixTimestamp.ToString()), 
        new Claim("exp", (nowInUnixTimestamp + (60*60)).ToString()), 
        new Claim("uid", uid) 
        }) 
      ); 

var tokenString = tokenHandler.WriteToken(token); 

poi provato ad accedere utente in Reagire applicazione nativa tramite Firebase Javascript SDK, con il seguente codice:

//omitting initialization code 
firebase.auth().signInWithCustomToken(firebaseJWT).catch(function(error) { 
      console.log('Error authenticating Firebase user. Code: ' + error.code + ' Message: ' + error.message);    
     }); 

ma ho ottenuto un errore dal Firebase dicendo:

errore autenticazione utente Firebase. Codice: auth/invalid-custom-token Messaggio: il formato token personalizzato non è corretto. Si prega di controllare la documentazione.

Anche l'esperimento con l'aggiunta di diverse attestazioni per il controllo della scadenza dei token non è stato d'aiuto.

Inoltre, ho provato a generare token con la libreria "dvsekhvalnov/jose-jwt" ma non riesco a farlo funzionare con l'algoritmo "RS256".

Quindi la domanda:

Qualsiasi suggerimento su quello che sto facendo di sbagliato?

+0

mi sono reso conto che il formato del token descritto dal collegamento http://stackoverflow.com/questions/37408684/is-it-still-possible-to-do-server-side-verification-of-tokens- in-firebase-3/37492640 # 37492640 è un token rilasciato da Firebase stesso, quindi la prima domanda non è più una domanda. –

+0

Ecco la risposta dall'assistenza di Google sulla stessa domanda: ** "Ho visto il tuo post in SO, che hai già una soluzione. Per il formato di token, devi sempre seguire ciò che è sulla documentazione più recente. per quanto riguarda l'autenticazione in questo momento e stiamo facendo del nostro meglio per mantenere le cose in movimento sulla nostra estremità. Tieni d'occhio le nostre note di rilascio per ulteriori aggiornamenti, e sentiti libero di ricontrollare con noi se necessario. "** Quindi sembra che la soluzione alternativa sia l'opzione migliore per ora. –

risposta

9

Questa soluzione .NET pura funziona per me, utilizzando le librerie Org.BouncyCastle (https://www.nuget.org/packages/BouncyCastle/) e Jose.JWT (https://www.nuget.org/packages/jose-jwt/).

ho seguito questi passi:

  • Nella console Firebase fare clic sull'icona 'cog', che è in alto a sinistra, accanto al nome del progetto e fare clic su 'Autorizzazioni'.
  • Nella pagina IAM e Amministrazione, fare clic su "Account servizio" a sinistra
  • Fare clic su "Crea account servizio" nella parte superiore, immettere un "Nome account servizio", selezionare "Progetto-> Editor" nella selezione Ruolo. , seleziona la casella "Fornisci una nuova chiave privata" e seleziona JSON
  • Fai clic su "Crea" e scarica il file JSON dell'account di servizio e tienilo al sicuro.
  • Aprire il file Account servizio JSON in un editor di testo adatto e mettere i valori nel codice seguente:

    // private_key from the Service Account JSON file 
    public static string [email protected]"-----BEGIN PRIVATE KEY-----\nMIIE...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n...\n-----END PRIVATE KEY-----\n"; 
    
    // Same for everyone 
    public static string firebasePayloadAUD="https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"; 
    
    // client_email from the Service Account JSON file 
    public static string firebasePayloadISS="[email protected]"; 
    public static string firebasePayloadSUB="serviceaccount[email protected]"; 
    
    // the token 'exp' - max 3600 seconds - see https://firebase.google.com/docs/auth/server/create-custom-tokens 
    public static int firebaseTokenExpirySecs=3600; 
    
    private static RsaPrivateCrtKeyParameters _rsaParams; 
    private static object _rsaParamsLocker=new object(); 
    
    void Main() { 
        // Example with custom claims 
        var uid="myuserid"; 
        var claims=new Dictionary<string, object> { 
         {"premium_account", true} 
        }; 
        Console.WriteLine(EncodeToken(uid, claims)); 
    } 
    
    public static string EncodeToken(string uid, Dictionary<string, object> claims) { 
        // Get the RsaPrivateCrtKeyParameters if we haven't already determined them 
        if (_rsaParams == null) { 
         lock (_rsaParamsLocker) { 
          if (_rsaParams == null) { 
           StreamReader sr = new StreamReader(GenerateStreamFromString(firebasePrivateKey.Replace(@"\n","\n"))); 
           var pr = new Org.BouncyCastle.OpenSsl.PemReader(sr); 
           _rsaParams = (RsaPrivateCrtKeyParameters)pr.ReadObject(); 
          } 
         } 
        } 
    
        var payload = new Dictionary<string, object> { 
         {"claims", claims} 
         ,{"uid", uid} 
         ,{"iat", secondsSinceEpoch(DateTime.UtcNow)} 
         ,{"exp", secondsSinceEpoch(DateTime.UtcNow.AddSeconds(firebaseTokenExpirySecs))} 
         ,{"aud", firebasePayloadAUD} 
         ,{"iss", firebasePayloadISS} 
         ,{"sub", firebasePayloadSUB} 
        }; 
    
        return Jose.JWT.Encode(payload, Org.BouncyCastle.Security.DotNetUtilities.ToRSA(_rsaParams), JwsAlgorithm.RS256); 
    } 
    
    private static long secondsSinceEpoch(DateTime dt) { 
        TimeSpan t = dt - new DateTime(1970, 1, 1); 
        return (long)t.TotalSeconds; 
    } 
    
    private static Stream GenerateStreamFromString(string s) { 
        MemoryStream stream = new MemoryStream(); 
        StreamWriter writer = new StreamWriter(stream); 
        writer.Write(s); 
        writer.Flush(); 
        stream.Position = 0; 
        return stream; 
    } 
    

per ottenere questo lavoro in IIS avevo bisogno di cambiare identità del pool dell'applicazione e impostare l'impostazione "Carica profilo utente" su true.

+0

Ciao, ho provato il tuo codice. Quando controllo il token generato in https://jwt.io/ sta dicendo firma non valida. –

+0

@ AjayPunekar il codice funziona ancora per me. Hai lavorato a questo? – Elliveny

+0

@Elliveny Ho provato il codice, il token è generato ma ottengo il messaggio di errore "Il formato del token personalizzato non è corretto" quando lo invio a Firebase, qualche idea? – RezaRahmati

2

non hanno trovato una risposta diretta per la domanda finora, quindi per ora finito con la seguente soluzione:

Using instruction here generato un file JSON con i dettagli dell'account di servizio e ha creato un server di base usando Node.js SDK del server Firebase che fa generare token personalizzati corretti per Firebase con il seguente codice:

var http = require('http'); 
var httpdispatcher = require('httpdispatcher'); 
var firebase = require('firebase'); 

var config = { 
    serviceAccount: { 
    projectId: "{projectId}", 
    clientEmail: "{projectServiceEmail}", 
    privateKey: "-----BEGIN PRIVATE KEY----- ... ---END PRIVATE KEY-----\n" 
    }, 
    databaseURL: "https://{projectId}.firebaseio.com" 
}; 

firebase.initializeApp(config);  

const PORT=8080; 

httpdispatcher.onGet("/firebaseCustomToken", function(req, res) { 
    var uid = req.params.uid; 

    if (uid) { 
     var customToken = firebase.auth().createCustomToken(uid); 
     res.writeHead(200, {'Content-Type': 'application/json'}); 
     res.end(JSON.stringify({'firebaseJWT' : customToken})); 
    } else { 
     res.writeHead(400, {'Content-Type': 'text/plain'}); 
     res.end('No uid parameter specified'); 
    } 
});  

function handleRequest(request, response){ 
    try { 
     //log the request on console 
     console.log(request.url); 
     //Disptach 
     httpdispatcher.dispatch(request, response); 
    } catch(err) { 
     console.log(err); 
    }  
} 

//create a server 
var server = http.createServer(handleRequest); 

//start our server 
server.listen(PORT, function(){  
    console.log("Server listening on: http://localhost:%s", PORT); 
}); 

Forse qualcuno troverà questa utile.

0

@ La risposta di Elliveny ha funzionato benissimo per me. Lo sto utilizzando in un'applicazione .NET Core 2.0 e ho costruito la risposta accettata per trasformare questa soluzione in una classe che può essere registrata come dipendenza singleton nel contenitore dei servizi app, così come la configurazione passata tramite il costruttore in modo che possiamo sfruttare i segreti .NET per la configurazione dello sviluppo locale e le variabili di ambiente per la configurazione di produzione.

Ho anche riordinato il flusso gestendo un po '.

Nota per NET core sviluppatori - è necessario utilizzare Portable.BouncyCastle

È possibile verificare i risultati codificati analizzando l'uscita JWT token con Jwt.IO

using Jose; 
using Org.BouncyCastle.Crypto.Parameters; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 

public class FirebaseTokenGenerator 
{ 
    // private_key from the Service Account JSON file 
    public static string firebasePrivateKey; 

    // Same for everyone 
    public static string firebasePayloadAUD = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"; 

    // client_email from the Service Account JSON file 
    public static string firebasePayloadISS; 
    public static string firebasePayloadSUB; 

    // the token 'exp' - max 3600 seconds - see https://firebase.google.com/docs/auth/server/create-custom-tokens 
    public static int firebaseTokenExpirySecs = 3600; 

    private static RsaPrivateCrtKeyParameters _rsaParams; 
    private static object _rsaParamsLocker = new object(); 

    public FirebaseTokenGenerator(string privateKey, string clientEmail) 
    { 
     firebasePrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); 
     firebasePayloadISS = clientEmail ?? throw new ArgumentNullException(nameof(clientEmail)); 
     firebasePayloadSUB = clientEmail; 
    } 

    public static string EncodeToken(string uid) 
    { 
     return EncodeToken(uid, null); 
    } 

    public static string EncodeToken(string uid, Dictionary<string, object> claims) 
    { 
     // Get the RsaPrivateCrtKeyParameters if we haven't already determined them 
     if (_rsaParams == null) 
     { 
      lock (_rsaParamsLocker) 
      { 
       if (_rsaParams == null) 
       { 
        using (var streamWriter = WriteToStreamWithString(firebasePrivateKey.Replace(@"\n", "\n"))) 
        { 
         using (var sr = new StreamReader(streamWriter.BaseStream)) 
         { 
          var pr = new Org.BouncyCastle.OpenSsl.PemReader(sr); 
          _rsaParams = (RsaPrivateCrtKeyParameters)pr.ReadObject(); 
         } 
        } 
       } 
      } 
     } 

     var payload = new Dictionary<string, object> { 
     {"uid", uid} 
     ,{"iat", SecondsSinceEpoch(DateTime.UtcNow)} 
     ,{"exp", SecondsSinceEpoch(DateTime.UtcNow.AddSeconds(firebaseTokenExpirySecs))} 
     ,{"aud", firebasePayloadAUD} 
     ,{"iss", firebasePayloadISS} 
     ,{"sub", firebasePayloadSUB} 
    }; 

     if (claims != null && claims.Any()) 
     { 
      payload.Add("claims", claims); 
     } 

     return JWT.Encode(payload, Org.BouncyCastle.Security.DotNetUtilities.ToRSA(_rsaParams), JwsAlgorithm.RS256); 
    } 


    private static long SecondsSinceEpoch(DateTime dt) 
    { 
     TimeSpan t = dt - new DateTime(1970, 1, 1); 
     return (long) t.TotalSeconds; 
    } 

    private static StreamWriter WriteToStreamWithString(string s) 
    { 
     MemoryStream stream = new MemoryStream(); 
     StreamWriter writer = new StreamWriter(stream); 
     writer.Write(s); 
     writer.Flush(); 
     stream.Position = 0; 
     return writer; 
    } 
} 
0

L'@ codice del Elliveny ha funzionato per me in locale ma in azzurro genera un errore: "Il sistema non riesce a trovare il file specificato". A causa del fatto che ho modificato un po 'il codice e ora funziona su entrambi i server.

private string EncodeToken(string uid, Dictionary<string, object> claims) 
    { 

     string jwt = string.Empty; 
     RsaPrivateCrtKeyParameters _rsaParams; 

     using (StreamReader sr = new StreamReader(GenerateStreamFromString(private_key.Replace(@"\n", "\n")))) 
     { 
      var pr = new Org.BouncyCastle.OpenSsl.PemReader(sr); 
      _rsaParams = (RsaPrivateCrtKeyParameters)pr.ReadObject(); 
     } 


     using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) 
     { 
      Dictionary<string, object> payload = new Dictionary<string, object> { 
       {"claims", claims} 
       ,{"uid", uid} 
       ,{"iat", secondsSinceEpoch(DateTime.UtcNow)} 
       ,{"exp", secondsSinceEpoch(DateTime.UtcNow.AddSeconds(firebaseTokenExpirySecs))} 
       ,{"aud", firebasePayloadAUD} 
       ,{"iss", client_email} 
       ,{"sub", client_email} 
      }; 

      RSAParameters rsaParams = DotNetUtilities.ToRSAParameters(_rsaParams); 
      rsa.ImportParameters(rsaParams); 
      jwt = JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256); 
     } 

     return jwt; 

    } 
Problemi correlati