facebook fun
diff --git a/scripts/lua/oauth/fake_facebook.lua b/scripts/lua/oauth/fake_facebook.lua
new file mode 100644
index 0000000..40f675c
--- /dev/null
+++ b/scripts/lua/oauth/fake_facebook.lua
@@ -0,0 +1,44 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+--   Permission is hereby granted, free of charge, to any person obtaining a
+--   copy of this software and associated documentation files (the "Software"),
+--   to deal in the Software without restriction, including without limitation
+--   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+--   and/or sell copies of the Software, and to permit persons to whom the
+--   Software is furnished to do so, subject to the following conditions:
+--
+--   The above copyright notice and this permission notice shall be included in
+--   all copies or substantial portions of the Software.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+local cjson = require 'cjson'
+local utils = require "lib/utils"
+
+function validateOAuthToken (token)
+
+  local headerName = utils.concatStrings({'http_', 'x-facebook-app-token'}):gsub("-", "_")
+ 
+  local facebookAppToken = ngx.var[headerName]
+  if facebookAppToken == nil then
+    request.err(401, 'Facebook requires you provide an app token to validate user tokens. Provide a X-Facebook-App-Token header')
+    return nil
+  end
+  if token == "token" and facebookAppToken == "app" then
+    return [[
+      {
+        "token":"good"
+      }
+    ]]
+  end
+  return nil
+end
+
+return validateOAuthToken
+
diff --git a/scripts/lua/policies/security/oauth2.lua b/scripts/lua/policies/security/oauth2.lua
index 89be891..b17d95a 100644
--- a/scripts/lua/policies/security/oauth2.lua
+++ b/scripts/lua/policies/security/oauth2.lua
@@ -34,19 +34,22 @@
 local REDIS_PASS = os.getenv("REDIS_PASS")
 
 -- Process the security object
--- @param securityObj security object from nginx conf file 
+-- @param securityObj security object from nginx conf file
 -- @return oauthId oauth identification
 local _M = {}
 
-function process(securityObj) 
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000) 
+function process(securityObj)
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
   local result = processWithRedis(red, securityObj)
   redis.close(red)
   return result
 end
 
-function processWithRedis(red, securityObj) 
-  
+function processWithRedis(red, securityObj)
+ 
+  local headerName = utils.concatStrings({'http_', 'x-facebook-app-token'}):gsub("-", "_")
+  local facebookAppToken = ngx.var[headerName]
+ 
   local accessToken = ngx.var['http_Authorization']
   if accessToken == nil then
     request.err(401, "No Authorization header provided")
@@ -55,37 +58,50 @@
   local token = {}
   local key = utils.concatStrings({"oauth:providers:", securityObj.provider, ":tokens:", accessToken})
   -- If we haven't cached the token go to the oauth provider
-  if not (redis.exists(red, key) == 1) then
-    token = exchange(accessToken, securityObj.provider)
-  else 
-    token = cjson.decode(redis.get(red, key))
+  if redis.exists(red, key) == 1 then
+    return cjson.decode(redis.get(red, key))
+  end
+ 
+  if securityObj.provider == 'facebook' or securityObj.provider == 'fake_facebook' then
+    if redis.exists(red, utils.concatStrings({key, facebookAppToken})) == 1  then
+      return cjson.decode(redis.get(red, utils.concatStrings({key, facebookAppToken})))
+    end
+  end
+ 
+  local token = exchange(accessToken, securityObj.provider)
+  if token == nil then
+    request.err(401, 'Token didn\'t work or provider doesn\'t support OpenID connect. ')
+    return nil
+  end
+ 
+  if token.error ~= nil then
+    request.err(401, 'Token didn\'t work or provider doesn\'t support OpenID connect. ')
+    return nil
   end
 
-  if token == nil or not (token.error == nil) then
-    request.err(401, "Token didn't work or provider doesn't support OpenID connect.") 
-    return false
+  if securityObj.provider == 'facebook' or securityObj.provider == 'fake_facebook' then
+    key = utils.concatStrings({key, facebookAppToken})
   end
+ 
   redis.set(red, key, cjson.encode(token))
-  
-  if not token.expires == nil then
+
+  if token.expires ~= nil then
     redis.expire(red, key, token.expires)
   end
-  return key
--- only check with the provider if we haven't cached the token. 
-
+  return token
 end
 
 --- Exchange tokens with an oauth provider. Loads a provider based on configuration in the nginx.conf
 -- @param token the accessToken passed in the authorization header of the routing request
 -- @param provider the name of the provider we will load from a file. Currently supported google/github/facebook
 -- @return the json object recieved from exchanging tokens with the provider
-  function exchange(token, provider) 
+  function exchange(token, provider)
     -- exchange tokens with the provider
     local loaded, provider = pcall(require, utils.concatStrings({'oauth/', provider}))
-   
-    if not loaded then 
+  
+    if not loaded then
       request.err(500, 'Error loading OAuth provider authentication module')
-      return 
+      return
     end
     local token = provider(token)
     -- cache the token
diff --git a/tests/scripts/lua/security.lua b/tests/scripts/lua/security.lua
index c5296d2..308a706 100644
--- a/tests/scripts/lua/security.lua
+++ b/tests/scripts/lua/security.lua
@@ -23,13 +23,13 @@
 local fakeredis = require 'fakeredis'
 local apikey = require 'policies/security/apiKey'
 local oauth = require 'policies/security/oauth2'
-local cjson = require "cjson" 
+local cjson = require "cjson"
 
-describe('API Key module', function() 
-  it('Checks an apiKey correctly', function() 
-    local red = fakeredis.new() 
-    local ngx = fakengx.new() 
-    local ngxattrs = cjson.decode([[ 
+describe('API Key module', function()
+  it('Checks an apiKey correctly', function()
+    local red = fakeredis.new()
+    local ngx = fakengx.new()
+    local ngxattrs = cjson.decode([[
       {
         "tenant":"abcd",
         "gatewayPath":"v1/test",
@@ -48,11 +48,11 @@
     red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
     local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
     assert.same(key, 'a1234')
-  end) 
-  it('Returns nil with a bad apikey', function() 
-    local red = fakeredis.new() 
-    local ngx = fakengx.new() 
-    local ngxattrs = cjson.decode([[ 
+  end)
+  it('Returns nil with a bad apikey', function()
+    local red = fakeredis.new()
+    local ngx = fakengx.new()
+    local ngxattrs = cjson.decode([[
       {
         "tenant":"abcd",
         "gatewayPath":"v1/test",
@@ -70,11 +70,11 @@
     red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
     local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
     assert.falsy(key)
-  end) 
-  it('Checks for a key with a custom header', function() 
-    local red = fakeredis.new() 
-    local ngx = fakengx.new() 
-    local ngxattrs = cjson.decode([[ 
+  end)
+  it('Checks for a key with a custom header', function()
+    local red = fakeredis.new()
+    local ngx = fakengx.new()
+    local ngxattrs = cjson.decode([[
       {
         "tenant":"abcd",
         "gatewayPath":"v1/test",
@@ -94,11 +94,11 @@
     red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
     local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
     assert.same(key, 'a1234')
-  end) 
-  it('Checks for a key with a custom header and hash configuration', function() 
-    local red = fakeredis.new() 
-    local ngx = fakengx.new() 
-    local ngxattrs = cjson.decode([[ 
+  end)
+  it('Checks for a key with a custom header and hash configuration', function()
+    local red = fakeredis.new()
+    local ngx = fakengx.new()
+    local ngxattrs = cjson.decode([[
       {
         "tenant":"abcd",
         "gatewayPath":"v1/test",
@@ -119,12 +119,12 @@
     red:set('subscriptions:tenant:abcd:api:bnez:key:fakehash', 'true')
     local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
     assert.same(key, 'fakehash')
-  end) 
-end) 
-describe('OAuth security module', function() 
+  end)
+end)
+describe('OAuth security module', function()
   it('Exchanges a good secret', function ()
-    local red = fakeredis.new() 
-    local token = "test" 
+    local red = fakeredis.new()
+    local token = "test"
     local ngxattrs = [[
       {
         "http_Authorization":"]] .. token .. [[",
@@ -133,7 +133,7 @@
       }
     ]]
     local ngx = fakengx.new()
-    ngx.var = cjson.decode(ngxattrs) 
+    ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
       {
@@ -142,13 +142,13 @@
         "scope":"resource"
       }
     ]]
-    local result = oauth.processWithRedis(red, cjson.decode(securityObj)) 
+    local result = oauth.processWithRedis(red, cjson.decode(securityObj))
     assert.same(red:exists('oauth:providers:mock:tokens:test'), 1)
     assert(result)
   end)
-  it('Exchanges a bad token, doesn\'t cache it and returns false', function() 
-    local red = fakeredis.new()   
-    local token = "bad" 
+  it('Exchanges a bad token, doesn\'t cache it and returns false', function()
+    local red = fakeredis.new()  
+    local token = "bad"
     local ngxattrs = [[
       {
         "http_Authorization":"]] .. token .. [[",
@@ -157,7 +157,7 @@
       }
     ]]
     local ngx = fakengx.new()
-    ngx.var = cjson.decode(ngxattrs) 
+    ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
       {
@@ -170,10 +170,89 @@
     assert.same(red:exists('oauth:providers:mock:tokens:bad'), 0)
     assert.falsy(result)
   end)
-end) 
-describe('Client Secret Module', function() 
+  it('Loads a facebook token from the cache without a valid app id', function()
+    local red = fakeredis.new()
+    local token = "test"
+    local ngxattrs = [[
+      {
+        "http_Authorization":"]] .. token .. [[",
+        "tenant":"1234",
+        "gatewayPath":"v1/test"
+      }
+    ]]
+    local ngx = fakengx.new()
+    ngx.var = cjson.decode(ngxattrs)
+    _G.ngx = ngx
+    local securityObj = [[
+      {
+        "type":"oauth2",
+        "provider":"fake_facebook",
+        "scope":"resource"
+      }
+    ]]
+    red:set('oauth:providers:fake_facebook:tokens:test', '{ "token":"good"}')
+    local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+    assert.truthy(result)
+  end)
+  it('Loads a facebook token from the cache with a valid app id', function()
+    local red = fakeredis.new()
+    local token = "test"
+    local appid = "app"
+    local ngxattrs = [[
+      {
+        "http_Authorization":"]] .. token .. [[",
+        "http_x_facebook_app_token":"]] .. appid .. [[",
+        "tenant":"1234",
+        "gatewayPath":"v1/test"
+      }
+    ]]
+    local ngx = fakengx.new()
+    ngx.var = cjson.decode(ngxattrs)
+    _G.ngx = ngx
+    local securityObj = [[
+      {
+        "type":"oauth2",
+        "provider":"fake_facebook",
+        "scope":"resource"
+      }
+    ]]
+    red:set('oauth:providers:fake_facebook:tokens:testapp', '{"token":"good"}')
+    local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+    assert.truthy(result)
+  end)
+
+  it('Exchanges a facebook token and sets it in redis correctly with the appid.', function()
+    local red = fakeredis.new()
+    local token = "token"
+    local appid = "app"
+    local ngxattrs = [[
+      {
+        "http_Authorization":"]] .. token .. [[",
+        "http_x_facebook_app_token":"]] .. appid .. [[",
+        "tenant":"1234",
+        "gatewayPath":"v1/test"
+      }
+    ]]
+    local ngx = fakengx.new()
+    ngx.var = cjson.decode(ngxattrs)
+    _G.ngx = ngx
+    local result = oauth.processWithRedis(red, cjson.decode(ngxattrs))
+    local securityObj = [[
+      {
+        "type":"oauth2",
+        "provider":"fake_facebook",
+        "scope":"resource"
+      }
+    ]]
+    local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+    assert.truthy(result)
+    assert.truthy(red:exists('oauth:providers:fake_facebook:tokens:tokenapp') == 1)
+    assert.truthy(red:exists('oauth:providers:fake_facebook:tokens:token') == 0)
+  end)
+end)
+describe('Client Secret Module', function()
   local clientSecret = require 'policies/security/clientSecret'
-  it('Validates a client secret pair with default names', function() 
+  it('Validates a client secret pair with default names', function()
     local ngx = fakengx.new()
     local red = fakeredis.new()
     local ngxattrs = [[
@@ -184,7 +263,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
-    ngx.var = cjson.decode(ngxattrs) 
+    ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
       {
@@ -193,12 +272,12 @@
       }
     ]]
     red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
-    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end) 
+    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
     assert(result)
-  end) 
-  it('Validates a client secret pair with new names', function() 
+  end)
+  it('Validates a client secret pair with new names', function()
     local ngx = fakengx.new()
-    local red = fakeredis.new() 
+    local red = fakeredis.new()
     local ngxattrs = [[
       {
         "http_test_id":"abcd",
@@ -217,11 +296,11 @@
         "secretFieldName":"test-secret"
       }
     ]]
-    red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true") 
-    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end) 
+    red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
+    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
     assert(result)
-  end) 
-  it('Doesn\'t work without a client id', function() 
+  end)
+  it('Doesn\'t work without a client id', function()
     local ngx = fakengx.new()
     local red = fakeredis.new()
     local ngxattrs = [[
@@ -231,7 +310,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
-    ngx.var = cjson.decode(ngxattrs) 
+    ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
       {
@@ -240,7 +319,7 @@
       }
     ]]
   end)
-  it('Doesn\'t work without a Client Secret', function() 
+  it('Doesn\'t work without a Client Secret', function()
     local ngx = fakengx.new()
     local red = fakeredis.new()
     local ngxattrs = [[
@@ -250,7 +329,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
-    ngx.var = cjson.decode(ngxattrs) 
+    ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
       {
@@ -259,7 +338,7 @@
       }
     ]]
     red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
-    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end) 
+    local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
     assert.falsy(result)
-  end) 
+  end)
 end)