Version 2 : 2011 : ThePhotoLabs / Phocoder (PfsTools)
Version 3 : 2013 : CloudHDR (Photomatix)
Version 1
PictoDroid
User Auth/Management
File Management
Processing Engine
Job Monitoring UI
Gallery Browsing UI
Editing UI
Set Splitter UI
Uploading UI
Cluster Management
Why Do Monoliths Form?
Inertia
Everything *seems* related
It's the right way to start! (Yes, really.)
Easier to on-board new devs
Easier to maintain
Easier to deploy
Easier or "Easier"?
Signs of Discomfort
Bloated Gemfile
Slow server/console start up
Long test runs
Long deploys
Changes cascading into seeminly unrelated areas
Fragile/high-churn code
Hard to on-board new devs
Application specific reasons
Version 2
Phocoder
Processing Engine
Job Monitoring UI
Cluster Management
ThePhotoLabs
User Auth/Management
File Management
Gallery Browsing UI
Editing UI
Set Splitter UI
Uploading UI
Lessons from Version 2
Phocoder
Very stable
Changes infrequently
Faster test runs
Faster deploys
ThePhotoLabs
Less stable (than Phocoder)
Frequent change
Faster test runs
Faster deploys
Integration between services can be... challenging
CloudHDR
Everything in a separate project
Roles of Projects
Central Services
Service Interfaces
Central Services
_users
_files
_processing
_director
Service Interfaces
_editor
_splitter
_processing_monitor
_uploads
_galleries
Technology of Projects
Traditional Rails
Rails JSON API
Ember
Traditional Rails + Ember
Traditional Rails
_users
_uploads
_director
Rails JSON API
_files
_processing
Ember
_editor
_splitter
_processing_monitor
Traditional Rails + Ember
_galleries
Authentication for SOA
OAuth(2)
Protected Content in the Monolith
Protected Content in the Monolith
browser->website: GET /protected
website->browser: REDIRECT /sign_in
Note over browser: Follow REDIRECT
browser->website: GET /sign_in
website->browser: 200 OK
Protected Content with OAuth2
Protected Content with OAuth2
browser->website: GET /protected
website-->browser: 200 OK /sign_in
Note over browser: Click "Sign in w Github"
browser->Github: GET github.com/confirm
Github->browser: 200 OK
Protected Content with only OAuth2
No 'local' sign in page
Protected Content with only OAuth2
browser->website: GET /protected
website->browser: REDIRECT github.com/confirm
Note over browser: Follow REDIRECT
browser->Github: GET github.com/confirm
Github->browser: 200 OK
Custom OAuth2 Provider
cloudhdr_users
# Gemfile
gem "devise"
gem "bootstrap-rails"
Two Jobs
UI for Sign In/Up/Out
OAuth2 Provider for Other Services
Job 1 : UI for Sign In/Up/Out
Feature: Sign Up
Scenario: Sucessful sign up
Given a user without an account
Given he has a valid invitation code
When he creates a new account with email "jeremy@octolabs.com"
Then he should see "Sign Out"
Feature: Sign In/Out
Scenario: Sucessful sign in then sign out
Given an existing user with email "jeremy@octolabs.com"
When he signs in with email "jeremy@octolabs.com"
Then he should see "Sign Out"
When he signs out
Then he should see "Sign In"
Job 2 : Auth Endpoint for Other Services
Feature: External App Sign In
Scenario: Sucessful sign in
Given an existing user with email "jeremy@octolabs.com"
Given a registered external app
When he hits the external auth endpoint
Then he should see "Sign In"
When he signs in with email "jeremy@octolabs.com"
Then he should be redirected back to the external app
This looks familiar
browser->_uploads: GET /
_uploads--> _users: Some Redirects
Note over _users: User not logged in
_users->browser: Sign In Form
browser->_users: POST credentials
Note over _users: More Redirects
_users-->_uploads: He's cool
_uploads->browser: 200 OK /
Job 2 : Auth Endpoint for Other Services
Feature: External App Sign In
Scenario: Token refresh (user is already signed in)
Given an existing and signed in user with email "jeremy@octolabs.com"
Given a registered external app
When he hits the external auth endpoint
Then he should be redirected back to the external app
Already logged in
browser->_uploads: GET /
_uploads--> _users: Some Redirects
Note over _users: User is logged in
Note over _users: More Redirects
_users-->_uploads: He's cool
_uploads->browser: 200 OK /
That's it for cloudhdr_users
(down for more sequence diagrams)
Request protected page
browser->_uploads: GET /
Note right of _uploads: Internal Redirects
_uploads-->browser: 302 REDIRECT _users/auth...
Land at Sign In Page
browser->_uploads: GET /
_uploads-->browser: 302 REDIRECT ...
Note left of browser: Follow redir
browser->_users: GET /auth?return_url=...
Note right of _users: Internal Redirs
_users->browser: 200 OK - Sign In
Sign In and Return
browser->_uploads: GET /
_uploads-->browser: 302 REDIRECT ...
browser->_users: GET /auth?return_url=...
_users->browser: 200 OK - Sign In
Note left of browser: Enter credentials
browser->_users: POST
_users-->browser: REDIRECT return_url
Arrive at protected content
browser->_uploads: Previous steps omitted
browser->_users: POST
_users-->browser: 302 REDIRECT return_url
Note left of browser: Follow REDIRECT
browser->_uploads: GET /?auth=...
_uploads->_users: VERIFY
_users->_uploads: 200 OK
_uploads->browser: 200 OK
browser->_uploads: GET /
_uploads-->browser: 302 REDIRECT
Note left of browser: Follow REDIRECT
browser->_users: GET /auth?return_url=...
_users-->browser: 302 REDIRECT return_url
Note left of browser: Follow REDIRECT
browser->_uploads: GET /
_uploads->_users: VERIFY
_users->_uploads: 200 OK
_uploads->browser: 200 OK
Using the Auth Endpoint from Another Service
# Gemfile
gem "cloudhdr_auth" # OAuth2 Client
class ApplicationController < CloudhdrAuth::BaseController
protect_from_forgery
end
class SecretStuffController < ApplicationController
before_filter :login_required
end
Two 'types' of sessions
cloudhdr_users : Signed In
cloudhdr_* : Having a session
SPA Authentication
SPA to Service
The _editor Ember app
running in the browser
talks directly to _files
Initial SPA Loading Sequence
Browser loads page/JS from cloudhdr_editor
JS function tries to pull profile json from cloudhdr_files
If that fails, redirect to cloudhdr_files auth endpoint
OAuth2 happens
Browser is redirected to cloudhdr_editor
JS function tries to pull profile json from cloudhdr_files
browser->_editor: GET /
_editor->browser: 200 OK
Note over browser: JS executes
browser->_files: GET /profile
_files->browser: 200 OK
Note over browser: App continues to load
Signed in, no session on _files
participant browser
participant _editor
participant _files
participant _users
browser->_files: GET /profile
_files->browser: 401 Not Authorized
Note over browser: JS Redirect
browser->_files: GET /auth
_files-->_users: Some redirects
_users-->_files: He's cool
_files-->_editor: Try again
Not signed in
participant browser
participant _editor
participant _files
participant _users
browser->_files: GET /profile
_files->browser: 401 Not Authorized
Note over browser: JS Redirect
browser->_files: GET /auth
_files-->_users: Some redirects
_users->browser: 200 OK /sign_in
# config/application.rb
config.middleware.use Rack::Cors do
allow do
origins(/http:\/\/localhost:\d*/,
/https:\/\/cloudhdr-\w*-octolabs\.fwd\.wf/,
/http:\/\/[\w-]*\.cloudhdr\.com/
)
resource '/api/v1/*',
:headers => :any,
:methods => [:get, :post, :put, :delete, :options]
end
end
Public/Open API
# config/application.rb
config.middleware.use Rack::Cors do
allow do
origins('*')
resource '/api/public/v1/*',
:headers => :any,
:methods => [:get]
end
end
Do NOT Do This!
# config/application.rb
config.middleware.use Rack::Cors do
allow do
origins('*')
resource '*',
:headers => :any,
:methods => [:get, :post, :put, :delete, :options]
end
end
Learn about Cross Site Scripting!
The AJAX Cookie Problem
By default jQuery does not send cookies with AJAX requests
withCredentials to the rescue!
// Make sure that cookies get sent with AJAX requests
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.xhrFields = { withCredentials : true };
});