So I'm trying to authenticate the Firebase REST API. I'm using the Vapor framework for server side swift and I installed the JWT package.
I'm trying to use the data in the serviceAccountKey.json
file and JWT to generate an auth token.
Here is the code I've tried:
let payload = try JSON(node: [
"iat": Date().timeIntervalSince1970,
"exp": Date().timeIntervalSince1970 + 3600,
"iss": "client_email from serviceAccountKey.json",
"aud": "https://accounts.google.com/o/oauth2/token",
"scope": [
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/userinfo.email"
]
])
let privateKey = "copied from serviceAccountKey.json"
let signer = try HS256(bytes: privateKey.bytes)
let jwt = try JWT(payload: payload, signer: signer)
let token = try jwt.createToken()
print(token)
serviceAccountKey.json
{
"type": "service_account",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": ""
}
You can create a custom token with the Firebase Admin SDK, or you can use a third-party JWT library if your server is written in a language which Firebase does not natively support. Custom tokens are signed JWTs where the private key used for signing belongs to a Google service account.
Firebase gives you complete control over authentication by allowing you to authenticate users or devices using secure JSON Web Tokens (JWTs). You generate these tokens on your server, pass them back to a client device, and then use them to authenticate via the signInWithCustomToken () method.
Using JWT to authenticate users Using Firebase to authenticate users Using Auth0 to authenticate users Using Okta to authenticate users Using Google ID tokens to authenticate users
When a user or device successfully signs in, Firebase creates a corresponding ID token that uniquely identifies them and grants them access to several resources, such as Firebase Realtime Database and Cloud Storage. You can re-use that ID token to identify the user or device on your custom backend server.
At this time I am using Xcode 8.3.3. Package.swift contains:
let package = Package(
name: "StripePayment",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5),
.Package(url:"https://github.com/vapor/jwt.git", majorVersion: 0,minor: 8),
.Package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", versions: Version(1, 0, 0)..<Version(3, .max, .max))
],
exclude: [
"Config",
"Database",
"Localization",
"Public",
"Resources",
"Tests",
]
)
If you generate a service account credential you need to have in mind the following, taken from https://cloud.google.com/storage/docs/authentication
: You can create a private key in the Cloud Platform Console by creating an OAuth Client ID for a service account. You can get your private key in JSON and PKCS12 format:
JSON keys are required if you are using Application Default Credentials in a production environment outside of Google Cloud Platform. JSON keys cannot be converted to other formats. PKCS12 (.p12) is supported by many different programming languages and libraries. If needed, you can convert the key into other formats using OpenSSL (see Converting the private key to other formats). However, PKCS12 keys cannot be converted to JSON format.
Note: You do NOT need to generate a service account at console.cloud.google.com . Just follow the steps 1...6 listed below.
Go to https://console.firebase.google.com , click on your project, next to Overview click on the wheel Settings, click on Service Accounts, scroll to the bottom of the page and click on Generate New Private Key.
Convert the p.12 (a.k.a pkcs12) file to .pem (a.k.a pkcs1) using OpenSSL
cat /path/to/xxxx-privatekey.p12 | openssl pkcs12 -nodes -nocerts -passin pass:notasecret | openssl rsa > /path/to/secret.pem
Go to github and search VaporJWT and import it in Xcode. It will help you create a signed JSON Web Token.
On this github page you will learn how to extract the private key for RSA use.
Convert .pem to deropenssl rsa -in /path/to/secret.pem -outform der -out /path/to/private.der
openssl base64 -in /path/to/private.der -out /path/to/Desktop/private.txt
``
import Vapor
import VaporJWT
let drop = Droplet()
var tokenID:String!
//set current date
let dateNow = Date()
// assign to expDate the validity period of the token returned by OAuth server (3600 seconds)
var expDate = String(Int(dateNow.timeIntervalSince1970 + (60 * 60)))
// assign to iatDate the time when the call was made to request an access token
var iatDate = String(Int(dateNow.timeIntervalSince1970))
// the header of the JSON Web Token (first part of the JWT)
let headerJWT = ["alg":"RS256","typ":"JWT"]
// the claim set of the JSON Web Token
let jwtClaimSet =
["iss":"[email protected]",
"scope":"https://www.googleapis.com/auth/firebase.database",
"aud":"https://www.googleapis.com/oauth2/v4/token",
"exp": expDate,
"iat": iatDate]
//Using VaporJWT construct a JSON Web Token and sign it with RS256 algorithm
//The only signing algorithm supported by the Google OAuth 2.0 Authorization
//Server is RSA using SHA-256 hashing algorithm.
let jwt = try JWT(headers: Node(node: headerJWT), payload: Node(node:jwtClaimSet), encoding: Base64URLEncoding(), signer: RS256(encodedKey: "copy paste here what you have in private.txt as explained at point 7 above "))
// create the JSON Web Token
let JWTtoken = try jwt.createToken()
let grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer" // this value must not be changed
let unreserved = "*-._"
let allowed = NSMutableCharacterSet.alphanumeric()
allowed.addCharacters(in: unreserved)
// percent or URL encode grant_type
let grant_URLEncoded = grant_type.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet)
// create a string made of grant_type and assertion. NOTE!!! only grant_type's value is URL encoded.
//JSON Web Token value does not need to be URL encoded
var fullString = "grant_type=\(grant_URLEncoded!)&assertion=\(JWTtoken)"
//pass fullString in the body parameter
drop.get("call") { request in
let response = try drop.client.post("https://www.googleapis.com/oauth2/v4/token", headers: ["Content-Type": "application/x-www-form-urlencoded"], query: [:],body: fullString)
let serverResp = response.headers
let serverBody = response.body.bytes
let serverJson = try JSON(bytes: serverBody!)
print(serverJson)
return "Success"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With