This repository has been archived by the owner on Sep 17, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
jwt.coffee
130 lines (111 loc) · 4.16 KB
/
jwt.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Node
crypto = require "crypto"
qstring = require "querystring"
# Lib
jwa = require "./jwa"
ju = require "./utils"
# version of the specification we are based on.
module.exports.spec_version = "draft-jones-json-web-token-10"
#
# Decodes a given JWT Token.
#
# ## Arguments
# * token : The encoded JWT.
#
# ## Returns
# * A **JWT Request** that holds the following.
# * Attributes: header, claim, segments.
# * Methods: verify( key ) where they key is *alogrithm* dependant. e.g. if RS you should use a valid *public PEM*
#
module.exports.decode = (token) ->
# check segments
segments = token.split '.'
throw new Error 'Not enough or too many segments' if segments.length != 3
# All segment should be base64
headerSeg = segments[0]
payloadSeg = segments[1]
signatureSeg = segments[2]
# base64 decode and parse JSON
header = JSON.parse ju.base64urlDecode(headerSeg)
claim = JSON.parse ju.base64urlDecode(payloadSeg)
# return
new JwtRequest( header, claim, segments )
#
#
# Creates a *JWT Token* given the *claim*, the *key* and the given *algorithm*. The *algorithm* defaults to
# `"HS256"` (Which is a *JWS* *HMAC* signature).
#
# # Rules for Creating a JWT
#
# To create a JWT, one MUST perform these steps. The order of the
# steps is not significant in cases where there are no dependencies
# between the inputs and outputs of the steps.
#
# 1. Create a JWT Claims Set containing the desired claims. Note that
# white space is explicitly allowed in the representation and no
# canonicalization is performed before encoding.
#
# 2. Let the Message be the bytes of the UTF-8 representation of the JWT Claims Set.
#
# 3. Create a JWT Header containing the desired set of header
# parameters. The JWT MUST conform to either the [JWS] or [JWE]
# specifications. Note that white space is explicitly allowed in
# the representation and no canonicalization is performed before
# encoding.
#
# 4. Base64url encode the bytes of the UTF-8 representation of the JWT
# Header. Let this be the Encoded JWT Header.
#
# 5. Depending upon whether the JWT is a JWS or JWE, there are two
# cases:
#
# * If the JWT is a JWS, create a JWS using the JWT Header as the
# JWS Header and the Message as the JWS Payload; all steps
# specified in [JWS] for creating a JWS MUST be followed.
#
# * Else, if the JWT is a JWE, create a JWE using the JWT Header
# as the JWE Header and the Message as the JWE Plaintext; all
# steps specified in [JWE] for creating a JWE MUST be followed.
#
# 6. If a nested signing or encryption operation will be performed,
# let the Message be the JWS or JWE, and return to Step 3, using a
# "typ" value of either "JWS" or "JWE" respectively in the new JWT
# Header created in that step.
#
# 7. Otherwise, let the resulting JWT be the JWS or JWE.
#
# Todo: Refactor to segregate the concerns between JWT and JWS.
# Todo: Include basic support for JWE identification (regardless of having implemented the JWE algorithms).
#
#
#
module.exports.encode = (claim, key, algorithm = "HS256", header_ext = {}) ->
jwa_provider = jwa.provider algorithm
throw new Error "Algorithm #{algorithm} is not yet supported." unless jwa_provider
jwa_signer = jwa_provider key
header =
typ: 'JWT'
alg: algorithm
for key, val of header_ext
header[key] = val
#create segments, all segment should be base64 string
segments = []
segments.push ju.base64urlEncode(JSON.stringify(header))
segments.push ju.base64urlEncode(JSON.stringify(claim))
jwa_signer.update( segments.join "." )
segments.push( jwa_signer.sign() )
segments.join('.')
#
# Abstracts the handling of a JWT Request.
#
class JwtRequest
constructor: (@header, @claim, @segments) ->
###
throw new Error "Unable to read `typ` form header or it doesn't match the expected 'JWT' value " unless @header.typ == 'JWT'
###
verify: (key) ->
_alg = @header?.alg
_alg = "none" unless _alg
_verifier = jwa.verifier _alg
throw new Error "Unable to find a verifier for algorithm #{_alg}" unless _verifier
_verifier.verify @, key