Skip to content
This repository has been archived by the owner on Aug 25, 2018. It is now read-only.

Commit

Permalink
Merge pull request #4 from firebase/require-uid
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
Chris Raynor committed Sep 15, 2014
2 parents f1c4ccd + 13319af commit cc8edc8
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 15 deletions.
17 changes: 10 additions & 7 deletions README.md
Expand Up @@ -34,16 +34,19 @@ this snippet of Ruby code:
```ruby
require "firebase_token_generator"

arbitraryAuthPayload = {:auth_data => "foo", :other_auth_data => "bar"}
payload = {:uid => "1", :auth_data => "foo", :other_auth_data => "bar"}

generator = Firebase::FirebaseTokenGenerator.new("<YOUR_FIREBASE_SECRET>")
token = generator.create_token(arbitraryAuthPayload)
token = generator.create_token(payload)
```

The arbitrary payload object passed into `create_token()` is then available for use within your
The payload passed to `create_token()` is made available for use within your
security rules via the [`auth` variable](https://www.firebase.com/docs/security/api/rule/auth.html).
This is how you pass trusted authentication details (e.g. the client's user ID) into your
Firebase rules.
This is how you pass trusted authentication details (e.g. the client's user ID)
to your Firebase security rules. The payload can contain any data of your
choosing, however it must contain a `:uid` key, which must be a string of less
than 256 characters. The generated token must be less than 1024 characters in
total.


## Token Options
Expand Down Expand Up @@ -72,9 +75,9 @@ Here is an example of how to use the second `options` argument:
```ruby
require "firebase_token_generator"

arbitraryAuthPayload = {:auth_data => "foo", :other_auth_data => "bar"}
payload = {:uid => "1", :auth_data => "foo", :other_auth_data => "bar"}
options = {:admin => true}

generator = Firebase::FirebaseTokenGenerator.new("<YOUR_FIREBASE_SECRET>")
token = generator.create_token(arbitraryAuthPayload, options)
token = generator.create_token(payload, options)
```
8 changes: 4 additions & 4 deletions firebase_token_generator.gemspec
@@ -1,11 +1,11 @@
Gem::Specification.new do |s|
s.name = "firebase_token_generator"
s.version = "1.0.3"
s.date = "2013-10-02"
s.version = "2.0.0"
s.date = "2014-09-04"
s.summary = "Generate Firebase Authentication Tokens"
s.description = "A library for generating signed authentication tokens for use with Firebase. Uses your app secret."
s.authors = ["Greg Soltis"]
s.email = "greg@firebase.com"
s.authors = ["Greg Soltis", "Chris Raynor"]
s.email = "support@firebase.com"
s.files = ["lib/firebase_token_generator.rb"]
s.homepage = "http://github.com/firebase/firebase-token-generator-ruby"
s.license = "MIT"
Expand Down
29 changes: 25 additions & 4 deletions lib/firebase_token_generator.rb
Expand Up @@ -11,6 +11,9 @@ class FirebaseTokenGenerator

# When creating an instance of the generator, you must provide your Firebase Application Secret
def initialize(secret)
if (!secret.is_a?(String))
raise ArgumentError, "FirebaseTokenGenerator: secret must be a string."
end
@secret = secret
end

Expand All @@ -24,16 +27,22 @@ def initialize(secret)
# [debug] If set to true, this client will receive debug information about the security rules
# [simulate] (internal-only for now) Runs security rules but makes no data changes
#
# Throws ArgumentError if given an invalid option
# Throws ArgumentError if given invalid data or an invalid option
# Throws RuntimeError if generated token is too long
def create_token(auth_data, options = {})
if auth_data.empty? and options.empty?
if (auth_data.nil? or auth_data.empty?) and (options.nil? or options.empty?)
raise ArgumentError, "FirebaseTokenGenerator.create_token: data is empty and no options are set. This token will have no effect on Firebase."
end
validate_auth_data(auth_data, (!options.nil? and options[:admin] == true))
claims = create_options_claims(options)
claims[:v] = TOKEN_VERSION
claims[:iat] = Time.now.to_i
claims[:d] = auth_data
encode_token(claims)
token = encode_token(claims)
if (token.length > 1024)
raise RuntimeError, "FirebaseTokenGenerator.create_token: generated token is too long."
end
token
end

private
Expand All @@ -50,6 +59,18 @@ def create_token(auth_data, options = {})
:simulate => :simulate
}

def validate_auth_data(auth_data, is_admin_token)
if (!auth_data.nil? && !auth_data.is_a?(Hash))
raise ArgumentError, "FirebaseTokenGenerator.create_token: auth data must be a hash"
end
contains_uid = (!auth_data.nil? and auth_data.has_key?(:uid))
if ((!contains_uid and !is_admin_token) or (contains_uid and !auth_data[:uid].is_a?(String)))
raise ArgumentError, "FirebaseTokenGenerator.create_token: auth data must contain a :uid key that must be a string."
elsif (contains_uid and (auth_data[:uid].length > 256))
raise ArgumentError, "FirebaseTokenGenerator.create_token: auth data must contain a :uid key that must not be longer than 256 bytes."
end
end

def create_options_claims(options)
opts = {}
options.each do |key, value|
Expand All @@ -59,7 +80,7 @@ def create_options_claims(options)
if CLAIMS_MAP.include?(key.to_sym) then
opts[CLAIMS_MAP[key.to_sym]] = value
else
raise ArgumentError, "#{key.to_s} is not a valid option"
raise ArgumentError, "FirebaseTokenGenerator.create_token: #{key.to_s} is not a valid option"
end
end
opts
Expand Down
69 changes: 69 additions & 0 deletions test/firebase_token_generator_test.rb
@@ -0,0 +1,69 @@
require_relative "../lib/firebase_token_generator"
require "test/unit"

class TestFirebaseTokenGenerator < Test::Unit::TestCase

def test_smoke_test
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
token = generator.create_token({:uid => "foo"})
end

def test_malformed_key
assert_raise( ArgumentError ) { Firebase::FirebaseTokenGenerator.new(1234567890) }
end

def test_no_uid
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( ArgumentError ) { generator.create_token({:blah => 5}) }
end

def test_invalid_uid
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( ArgumentError ) { generator.create_token({:uid => 5, :blah => 5}) }
end

def test_uid_max_length
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
#length: 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 256
token = generator.create_token({:uid => "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"})
end

def test_uid_too_long
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
#length: 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 257
assert_raise( ArgumentError ) { generator.create_token({:uid => "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567"}) }
end

def test_uid_min_length
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
token = generator.create_token({:uid => ""})
end

def test_token_too_long
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( RuntimeError ) { generator.create_token({:uid => "blah", :long_var => "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345612345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234561234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"}) }
end

def test_no_uid_with_admin
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
token = generator.create_token(nil, {:admin => true})
token = generator.create_token({}, {:admin => true})
token = generator.create_token({:foo => "bar"}, {:admin => true})
end

def test_invalid_uid_with_admin_1
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( ArgumentError ) { generator.create_token({:uid => 1}, {:admin => true}) }
end

def test_invalid_uid_with_admin_2
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( ArgumentError ) { generator.create_token({:uid => nil}, {:admin => true}) }
end

def test_invalid_uid_with_admin_3
generator = Firebase::FirebaseTokenGenerator.new("barfoo")
assert_raise( ArgumentError ) { generator.create_token("foo", {:admin => true}) }
end

end

0 comments on commit cc8edc8

Please sign in to comment.