Skip to content

Commit 37f6bf9

Browse files
committed
Merge pull request #1 from cakejelly/identity-token
Error class fix and class for generating layer identity tokens
2 parents 55f2f10 + 0570eed commit 37f6bf9

File tree

7 files changed

+169
-2
lines changed

7 files changed

+169
-2
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ layer.block_user("owner_id", "user_id")
142142
layer.unblock_user("owner_id", "user_id")
143143
```
144144

145+
### Generating Identity Tokens
146+
See: [the official authentication guide](https://developer.layer.com/docs/android/guides#authentication)
147+
148+
Make sure the following environment variables are set:
149+
`ENV['LAYER_KEY_ID']`
150+
`ENV['LAYER_PROVIDER_ID']`
151+
`ENV['LAYER_PRIVATE_KEY']`
152+
153+
```ruby
154+
# Returns a valid signed identity token.
155+
layer.generate_identity_token(user_id: "1234", nonce: "your_random_nonce")
156+
```
157+
145158
## Development
146159

147160
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

layer-api.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
2727
spec.add_development_dependency "pry", "~> 0.10.1"
2828

2929
spec.add_dependency "faraday", "~> 0.9.1"
30+
spec.add_dependency "jwt", "~> 1.5.1"
3031
end

lib/layer/api.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'faraday'
22
require 'json'
3+
require 'jwt'
34

45
require "layer/api/version"
56
require "layer/api/configuration"

lib/layer/api/client.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "layer/api/client/conversations"
22
require "layer/api/client/announcements"
33
require "layer/api/client/users"
4+
require "layer/api/client/identity_token"
45

56
module Layer
67
module Api
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module Layer
2+
module Api
3+
class IdentityToken
4+
attr_reader :user_id, :nonce, :expires_at
5+
6+
def initialize(options = {})
7+
@user_id = options[:user_id]
8+
@nonce = options[:nonce]
9+
@expires_at = (options[:expires_at] || Time.now+(1209600))
10+
end
11+
12+
def to_s
13+
get_jwt
14+
end
15+
16+
def layer_key_id
17+
ENV['LAYER_KEY_ID']
18+
end
19+
20+
def layer_provider_id
21+
ENV['LAYER_PROVIDER_ID']
22+
end
23+
24+
private
25+
26+
def get_jwt
27+
JWT.encode(claim, private_key, 'RS256', headers)
28+
end
29+
30+
def headers
31+
{
32+
typ: 'JWT',
33+
cty: 'layer-eit;v=1',
34+
kid: layer_key_id
35+
}
36+
end
37+
38+
def claim
39+
{
40+
iss: layer_provider_id,
41+
prn: user_id.to_s,
42+
iat: Time.now.to_i,
43+
exp: expires_at.to_i,
44+
nce: nonce
45+
}
46+
end
47+
48+
def private_key
49+
# Cloud66 stores newlines as \n instead of \\n
50+
key = ENV['LAYER_PRIVATE_KEY'].dup
51+
OpenSSL::PKey::RSA.new(key.gsub!("\\n","\n"))
52+
end
53+
end
54+
55+
class Client
56+
def generate_identity_token(options = {})
57+
IdentityToken.new(options).to_s
58+
end
59+
end
60+
end
61+
end

lib/layer/api/error.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ def self.from_response(response)
1010
when 500..599 then Layer::Api::ServerError
1111
else self
1212
end
13+
klass.new(response)
1314
end
14-
15-
klass.new(response)
1615
end
1716

1817
def initialize(response)

spec/layer/identity_token_spec.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
require 'spec_helper'
2+
3+
describe Layer::Api::IdentityToken do
4+
describe ".new" do
5+
it "should allow you to set the user_id, nonce and expires_at variables" do
6+
user_id = "1234"
7+
nonce = "your_random_nonce"
8+
expires_at = "12345678"
9+
10+
token = Layer::Api::IdentityToken.new(
11+
user_id: user_id,
12+
nonce: nonce,
13+
expires_at: expires_at
14+
)
15+
16+
expect(token.user_id).to eq(user_id)
17+
expect(token.nonce).to eq(nonce)
18+
expect(token.expires_at).to eq(expires_at)
19+
end
20+
end
21+
22+
describe ".layer_key_id" do
23+
it "should return your ENV['LAYER_KEY_ID']" do
24+
layer_key_id = Layer::Api::IdentityToken.new.layer_key_id
25+
expect(layer_key_id).to eq(ENV['LAYER_KEY_ID'])
26+
end
27+
end
28+
29+
describe ".layer_provider_id" do
30+
it "should return your ENV['LAYER_PROVIDER_ID']" do
31+
provider_id = Layer::Api::IdentityToken.new.layer_provider_id
32+
expect(provider_id).to eq(ENV['LAYER_PROVIDER_ID'])
33+
end
34+
end
35+
36+
describe ".headers" do
37+
it "should return necessary headers" do
38+
token = Layer::Api::IdentityToken.new
39+
40+
headers = token.send(:headers)
41+
42+
expect(headers[:kid]).to eq(ENV['LAYER_KEY_ID'])
43+
expect(headers[:cty]).to eq('layer-eit;v=1')
44+
expect(headers[:typ]).to eq('JWT')
45+
end
46+
end
47+
48+
describe ".claim" do
49+
it "should return necessary payload" do
50+
token = Layer::Api::IdentityToken.new(
51+
user_id: "user_id",
52+
nonce: "nonce",
53+
expires_at: 1234567
54+
)
55+
56+
claim = token.send(:claim)
57+
58+
expect(claim[:iss]).to eq(token.layer_provider_id)
59+
expect(claim[:prn]).to eq(token.user_id)
60+
expect(claim[:exp]).to eq(token.expires_at)
61+
expect(claim[:nce]).to eq(token.nonce)
62+
end
63+
end
64+
65+
describe ".private_key" do
66+
it "should return valid rsa private key" do
67+
key = Layer::Api::IdentityToken.new.send(:private_key)
68+
expect(key).to be_instance_of(OpenSSL::PKey::RSA)
69+
end
70+
end
71+
72+
describe ".to_s" do
73+
it "should return a string representation of the identity token" do
74+
token = Layer::Api::IdentityToken.new.to_s
75+
expect(token).to be_instance_of(String)
76+
end
77+
end
78+
79+
describe ".generate_identity_token" do
80+
it "should return the correct IdentityToken" do
81+
options = {}
82+
options[:user_id] = "user_id"
83+
options[:nonce] = "user_id"
84+
layer = Layer::Api::Client.new
85+
expected_token = Layer::Api::IdentityToken.new(options).to_s
86+
actual_token = layer.generate_identity_token(options)
87+
88+
expect(actual_token).to eq(expected_token)
89+
end
90+
end
91+
end

0 commit comments

Comments
 (0)