Skip to content

Commit

Permalink
Add support for customer login
Browse files Browse the repository at this point in the history
* Add instance method on Customer object to generate
  a login token for that customer. This only works if
  the app has the store_v2_customer_login scope and is
  installed on the store.
  • Loading branch information
mattolson committed Jul 8, 2016
1 parent 62a84f5 commit 9b01ea7
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ pkg/*
tmp/*
.env
.env-*
vendor/bundle
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Your contribution here.

* [#000](https://github.com/bigcommerce/bigcommerce-api-ruby/pull/000): Brief description here. - [@username](https://github.com/username).
* [#128](https://github.com/bigcommerce/bigcommerce-api-ruby/pull/128): Added support for token generation for storefront login. - [@mattolson](https://github.com/mattolson).

## 1.0.0
Please note that this is the start of a new major release which breaks all backward compatibility.
Expand Down
3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Contributing to Bigcommerce


We would love to see contributors! You're encouraged to submit [pull requests](https://github.com/bigcommerce/bigcommerce-api-ruby/pulls), [propose features, and discuss issues](https://github.com/bigcommerce/bigcommerce-api-ruby/issues).
We welcome your contribution! You're encouraged to submit [pull requests](https://github.com/bigcommerce/bigcommerce-api-ruby/pulls), [propose features, and discuss issues](https://github.com/bigcommerce/bigcommerce-api-ruby/issues).

#### Fork the Project

Expand Down
1 change: 1 addition & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Many thanks to the contributors and authors of the following libraries!
- [Faraday](https://github.com/lostisland/faraday) Simple, but flexible HTTP client library, with support for multiple backends. - [MIT](https://github.com/lostisland/faraday/blob/master/LICENSE.md)
- [Faraday Middleware](https://github.com/lostisland/faraday_middleware) Various Faraday middlewares for Faraday-based API wrappers. - [MIT](https://github.com/lostisland/faraday_middleware/blob/master/LICENSE.md)
- [Hashie](https://github.com/intridea/hashie) Hashie is a collection of classes and mixins that make hashes more powerful. - [MIT](https://github.com/intridea/hashie/blob/master/LICENSE)
- [JWT](https://github.com/jwt/ruby-jwt) Used to encode and sign JWT tokens for the Customer Login API. - [MIT](https://github.com/jwt/ruby-jwt/blob/master/LICENSE)
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ For more information about configuring SSL with Faraday, please see the followin
- [Faraday SSL example](https://gist.github.com/mislav/938183)
- [Faraday: Setting up SSL certificates](https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates)

### Customer Login API
If you want to generate tokens for storefront login using the Customer Login API, you need to configure your app's client secret.

- ```store_hash```: The store hash of the store you are operating against.
- ```client_id```: Obtained from the on the BigCommerce [Developer Portal's](http://developer.bigcommerce.com) "My Apps" section.
- ```client_secret```: Obtained from the on the BigCommerce [Developer Portal's](http://developer.bigcommerce.com) "My Apps" section.

```rb
Bigcommerce.configure do |config|
config.store_hash = ENV['BC_STORE_HASH']
config.client_id = ENV['BC_CLIENT_ID']
config.client_secret = ENV['BC_CLIENT_SECRET']
end
```

## Usage
For full examples of using the API client, please see the [examples folder](examples) and refer to BigCommerce's [developer documentation](https://developer.bigcommerce.com/api).
Expand Down Expand Up @@ -124,8 +138,8 @@ This connection is nothing more than a `Faraday::Connection` – so if you want
```rb
connection = Bigcommerce::Connection.build(
Bigcommerce::Config.new(
store_hash: ENV['BC_STORE_HASH'],
client_id: ENV['BC_CLIENT_ID'],
store_hash: ENV['BC_STORE_HASH'],
client_id: ENV['BC_CLIENT_ID'],
access_token: ENV['BC_ACCESS_TOKEN']
)
)
Expand All @@ -143,7 +157,7 @@ Bigcommerce::System.raw_request(:get, 'time', connection: connection)
```rb
connection_legacy = Bigcommerce::Connection.build(
Bigcommerce::Config.new(
auth: 'legacy',
auth: 'legacy',
url: ENV['BC_API_ENDPOINT_LEGACY'],
username: ENV['BC_USERNAME'],
api_key: ENV['BC_API_KEY']
Expand All @@ -159,4 +173,4 @@ Bigcommerce::System.raw_request(:get, 'time', connection: connection_legacy)
```

## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
See [CONTRIBUTING.md](CONTRIBUTING.md)
1 change: 1 addition & 0 deletions bigcommerce.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ Gem::Specification.new do |s|
s.add_dependency 'faraday', '~> 0.9'
s.add_dependency 'faraday_middleware', '~> 0.10.0'
s.add_dependency 'hashie', '~> 3.4'
s.add_dependency 'jwt', '~> 1.5.4'
end
14 changes: 14 additions & 0 deletions examples/customers/customer_login.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'bigcommerce'

Bigcommerce.configure do |config|
config.store_hash = ENV['BC_STORE_HASH']
config.client_id = ENV['BC_CLIENT_ID']
config.client_secret = ENV['BC_CLIENT_SECRET']
config.access_token = ENV['BC_ACCESS_TOKEN']
end

# Get a customer
customer = Bigcommerce::Customer.all(page: 1).first

# Generate token login url
puts customer.login_token
2 changes: 1 addition & 1 deletion lib/bigcommerce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Bigcommerce
Dir.glob(resources, &method(:require))

class << self
attr_reader :api
attr_reader :api, :config

def configure
@config = Bigcommerce::Config.new.tap { |h| yield(h) }
Expand Down
19 changes: 19 additions & 0 deletions lib/bigcommerce/resources/customers/customer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'jwt'
require 'securerandom'

# Customer
# Identity and account details for customers shopping at a Bigcommerce store.
# https://developer.bigcommerce.com/api/stores/v2/customers
Expand Down Expand Up @@ -26,5 +29,21 @@ class Customer < Resource
def self.count(params = {})
get 'customers/count', params
end

# Generate a token that can be used to log the customer into the storefront.
# This requires your app to have the store_v2_customers_login scope and to
# be installed in the store.
def login_token(config: Bigcommerce.config)
payload = {
'iss' => config.client_id,
'iat' => Time.now.to_i,
'jti' => SecureRandom.uuid,
'operation' => 'customer_login',
'store_hash' => config.store_hash,
'customer_id' => id
}

JWT.encode(payload, config.client_secret, 'HS256')
end
end
end
33 changes: 29 additions & 4 deletions spec/bigcommerce/unit/resources/customers/customer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
RSpec.describe Bigcommerce::Customer do
before(:each) { @customer = Bigcommerce::Customer }

describe '.count' do
it 'should hit the correct path' do
expect(@customer).to receive(:get).with('customers/count', {})
@customer.count
expect(described_class).to receive(:get).with('customers/count', {})
described_class.count
end
end

describe '.login_token' do
let(:client_id) { SecureRandom.hex(6) }
let(:client_secret) { SecureRandom.hex(6) }
let(:store_hash) { SecureRandom.hex(4) }
let(:customer_id) { Random.rand(1000) }
let(:customer) { described_class.new(id: customer_id) }
subject { customer.login_token }

before do
Bigcommerce.configure do |config|
config.store_hash = store_hash
config.client_id = client_id
config.client_secret = client_secret
end
end

it 'should generate a signed token with the right payload' do
payload = JWT.decode(subject, client_secret, true, { :algorithm => 'HS256' })[0]
expect(payload['iss']).to eq(client_id)
expect(payload['store_hash']).to eq(store_hash)
expect(payload['operation']).to eq('customer_login')
expect(payload['customer_id']).to eq(customer_id)
expect(payload['iat']).to be <= Time.now.to_i
expect(payload['jti']).to_not be_empty
end
end
end

0 comments on commit 9b01ea7

Please sign in to comment.