Skip to content

Commit

Permalink
Refactor generate_auth_token
Browse files Browse the repository at this point in the history
  • Loading branch information
Amir Tocker committed Feb 22, 2017
1 parent 1182e0a commit 0a0080e
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 28 deletions.
20 changes: 14 additions & 6 deletions lib/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/utils.js.map

Large diffs are not rendered by default.

37 changes: 21 additions & 16 deletions src/auth_token.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ crypto = require('crypto')
config = require('./config')

digest = (message, key) ->
crypto.createHmac("sha256", new Buffer(key, "hex"))
.update message
.digest 'hex'
crypto.createHmac("sha256", new Buffer(key, "hex")).update( message).digest('hex')

###*
* Escape url using lowercase hex code
* @param {string} url a url string
* @return escaped url
###
escape_to_lower = (url) ->
encodeURIComponent(url).replace(/%../g, (match)-> match.toLowerCase())

###*
* Generate an authorization token
Expand All @@ -23,25 +29,24 @@ digest = (message, key) ->
* @returns {string} the authorization token
###
module.exports = (options)->
params = Object.assign {}, config().auth_token, options
tokenName = params.token_name ? "__cld_token__"
tokenName = options.token_name ? "__cld_token__"

unless params.expiration?
if params.duration?
start = params.start_time ? Math.round(Date.now() / 1000)
params.expiration = start + params.duration
unless options.expiration?
if options.duration?
start = options.start_time ? Math.round(Date.now() / 1000)
options.expiration = start + options.duration
else
throw new Error( "Must provide either expiration or duration")

tokenParts = []
tokenParts.push("ip=#{params.ip}") if params.ip?
tokenParts.push("st=#{params.start_time}") if params.start_time?
tokenParts.push("exp=#{params.expiration}")
tokenParts.push("acl=#{params.acl}") if params.acl?
tokenParts.push("ip=#{options.ip}") if options.ip?
tokenParts.push("st=#{options.start_time}") if options.start_time?
tokenParts.push("exp=#{options.expiration}")
tokenParts.push("acl=#{escape_to_lower(options.acl)}") if options.acl?
toSign = (part for part in tokenParts)
if params.url
url = encodeURIComponent(params.url).replace(/%../g, (match)-> match.toLowerCase())
if options.url
url = escape_to_lower(options.url)
toSign.push "url=#{url}"
auth = digest(toSign.join("~"), params.key)
auth = digest(toSign.join("~"), options.key)
tokenParts.push("hmac=#{auth}")
"#{tokenName}=#{tokenParts.join('~')}"
13 changes: 10 additions & 3 deletions src/utils.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ querystring = require('querystring')
url = require('url')

utils = exports
exports.generate_auth_token = require("./auth_token")
generate_token = require("./auth_token")
exports.generate_auth_token = (options)->
token_options = Object.assign {}, config().auth_token, options
generate_token token_options

exports.CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"
exports.OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"
exports.AKAMAI_SHARED_CDN = "res.cloudinary.com"
Expand Down Expand Up @@ -396,7 +400,9 @@ exports.url = (public_id, options = {}) ->
api_secret = utils.option_consume(options, "api_secret", config().api_secret)
url_suffix = utils.option_consume(options, "url_suffix")
use_root_path = utils.option_consume(options, "use_root_path", config().use_root_path)
auth_token = if options.auth_token == false then false else exports.merge config().auth_token, utils.option_consume(options, "auth_token")
auth_token = utils.option_consume(options, "auth_token")
if auth_token != false
auth_token = exports.merge config().auth_token, auth_token

preloaded = /^(image|raw)\/([a-z0-9_]+)\/v(\d+)\/([^#]+)$/.exec(public_id)
if preloaded
Expand Down Expand Up @@ -436,7 +442,8 @@ exports.url = (public_id, options = {}) ->
resultUrl = [prefix, resource_type, type, signature, transformation, version,
public_id].filter((part) -> part? && part != '').join('/')
if sign_url && !_.isEmpty(auth_token)
token = utils.generate_auth_token exports.merge(url: url.parse(resultUrl).path, auth_token)
auth_token.url = url.parse(resultUrl).path
token = generate_token( auth_token)
resultUrl += "?#{token}"
resultUrl

Expand Down
4 changes: 2 additions & 2 deletions test/authtoken_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe "authToken", ->

it "should generate with start and window", ->
token = utils.generate_auth_token start_time: 1111111111, acl: "/image/*", duration: 300
expect(token).to.eql '__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af'
expect(token).to.eql "__cld_token__=st=1111111111~exp=1111111411~acl=%2fimage%2f*~hmac=1751370bcc6cfe9e03f30dd1a9722ba0f2cdca283fa3e6df3342a00a7528cc51"

describe "authenticated url", ->
beforeEach ->
Expand Down Expand Up @@ -56,7 +56,7 @@ describe "authToken", ->
tokenOptions = {key: KEY, duration: 300, acl: "/*/t_#{user}"}
tokenOptions.start_time = 222222222 # we can't rely on the default "now" value in tests
cookieToken = utils.generate_auth_token tokenOptions
expect(cookieToken).to.eql("__cld_token__=st=222222222~exp=222222522~acl=/*/t_foobar~hmac=eb5e2266c8ec9573f696025f075b92998080347e1c12ac39a26c94d7d712704a")
expect(cookieToken).to.eql("__cld_token__=st=222222222~exp=222222522~acl=%2f*%2ft_foobar~hmac=8e39600cc18cec339b21fe2b05fcb64b98de373355f8ce732c35710d8b10259f")

it "should add token to an image tag url", ->
tag = cloudinary.image "sample.jpg", sign_url: true, type: "authenticated", version: "1486020273"
Expand Down

0 comments on commit 0a0080e

Please sign in to comment.