1package authn 2 3import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "time" 8) 9 10const ( 11 TimeFormatToken4 = "2006-01-02 15:04:05 MST" 12) 13 14type AuthnToken interface { 15 // Parse from JSON. Required before further usage. 16 FromJSON(data []byte) error 17 // Raw token as obtained from the authentication service. 18 Raw() []byte 19 // Whether the token will expire soon. 20 ShouldRefresh() bool 21} 22 23type AuthnToken4 struct { 24 bytes []byte 25 Data string `json:"data"` 26 Signature string `json:"signature"` 27 Key string `json:"key"` 28 Timestamp time.Time 29} 30 31// Sample token 32// {"protected":"eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiI5M2VjNTEwODRmZTM3Zjc3M2I1ODhlNTYyYWVjZGMxMSJ9","payload":"eyJzdWIiOiJhZG1pbiIsImlhdCI6MTUxMDc1MzI1OX0=","signature":"raCufKOf7sKzciZInQTphu1mBbLhAdIJM72ChLB4m5wKWxFnNz_7LawQ9iYEI_we1-tdZtTXoopn_T1qoTplR9_Bo3KkpI5Hj3DB7SmBpR3CSRTnnEwkJ0_aJ8bql5Cbst4i4rSftyEmUqX-FDOqJdAztdi9BUJyLfbeKTW9OGg-QJQzPX1ucB7IpvTFCEjMoO8KUxZpbHj-KpwqAMZRooG4ULBkxp5nSfs-LN27JupU58oRgIfaWASaDmA98O2x6o88MFpxK_M0FeFGuDKewNGrRc8lCOtTQ9cULA080M5CSnruCqu1Qd52r72KIOAfyzNIiBCLTkblz2fZyEkdSKQmZ8J3AakxQE2jyHmMT-eXjfsEIzEt-IRPJIirI3Qm"} 33// https://www.conjur.org/reference/cryptography.html 34type AuthnToken5 struct { 35 bytes []byte 36 Protected string `json:"protected"` 37 Payload string `json:"payload"` 38 Signature string `json:"signature"` 39 iat time.Time 40 exp *time.Time 41} 42 43func hasField(fields map[string]string, name string) (hasField bool) { 44 _, hasField = fields[name] 45 return 46} 47 48func NewToken(data []byte) (token AuthnToken, err error) { 49 fields := make(map[string]string) 50 if err = json.Unmarshal(data, &fields); err != nil { 51 err = fmt.Errorf("Unable to unmarshal token : %s", err) 52 return 53 } 54 55 if hasField(fields, "protected") && hasField(fields, "payload") && hasField(fields, "signature") { 56 t := &AuthnToken5{} 57 token = t 58 } else if hasField(fields, "data") && hasField(fields, "timestamp") && hasField(fields, "signature") && hasField(fields, "key") { 59 t := &AuthnToken4{} 60 token = t 61 } else { 62 err = fmt.Errorf("Unrecognized token format") 63 return 64 } 65 66 err = token.FromJSON(data) 67 68 return 69} 70 71func (t *AuthnToken5) FromJSON(data []byte) (err error) { 72 t.bytes = data 73 74 err = json.Unmarshal(data, &t) 75 if err != nil { 76 err = fmt.Errorf("Unable to unmarshal v5 access token %s", err) 77 return 78 } 79 80 // Example: {"sub":"admin","iat":1510753259} 81 payloadFields := make(map[string]interface{}) 82 var payloadJSON []byte 83 payloadJSON, err = base64.StdEncoding.DecodeString(t.Payload) 84 if err != nil { 85 err = fmt.Errorf("v5 access token field 'payload' is not valid base64") 86 return 87 } 88 err = json.Unmarshal(payloadJSON, &payloadFields) 89 if err != nil { 90 err = fmt.Errorf("Unable to unmarshal v5 access token field 'payload' : %s", err) 91 return 92 } 93 94 iat_v, ok := payloadFields["iat"] 95 if !ok { 96 err = fmt.Errorf("v5 access token field 'payload' does not contain 'iat'") 97 return 98 } 99 iat_f := iat_v.(float64) 100 // In the absence of exp, the token expires at iat+8 minutes 101 t.iat = time.Unix(int64(iat_f), 0) 102 103 exp_v, ok := payloadFields["exp"] 104 if ok { 105 exp_f := exp_v.(float64) 106 exp := time.Unix(int64(exp_f), 0) 107 t.exp = &exp 108 if t.iat.After(*t.exp) { 109 err = fmt.Errorf("v5 access token expired before it was issued") 110 return 111 } 112 } 113 114 return 115} 116 117func (t *AuthnToken4) FromJSON(data []byte) (err error) { 118 err = json.Unmarshal(data, &t) 119 if err != nil { 120 err = fmt.Errorf("Unable to unmarshal v4 access token %s", err) 121 } 122 123 return 124} 125 126func (t *AuthnToken4) UnmarshalJSON(data []byte) (err error) { 127 type Alias AuthnToken4 128 x := &struct { 129 Data string `json:"data"` 130 Timestamp string `json:"timestamp"` 131 Signature string `json:"signature"` 132 Key string `json:"key"` 133 *Alias 134 }{ 135 Alias: (*Alias)(t), 136 } 137 138 if err = json.Unmarshal(data, &x); err != nil { 139 return 140 } 141 142 t.Timestamp, err = time.Parse(TimeFormatToken4, x.Timestamp) 143 t.bytes = data 144 145 return 146} 147 148func (t *AuthnToken5) Raw() []byte { 149 return t.bytes 150} 151 152func (t *AuthnToken5) ShouldRefresh() bool { 153 if t.exp != nil { 154 // Expire when the token is 85% expired 155 lifespan := t.exp.Sub(t.iat) 156 duration := float32(lifespan) * 0.85 157 return time.Now().After(t.iat.Add(time.Duration(duration))) 158 } else { 159 // Token expires 8 minutes after issue, by default 160 return time.Now().After(t.iat.Add(5 * time.Minute)) 161 } 162} 163 164func (t *AuthnToken4) Raw() []byte { 165 return t.bytes 166} 167 168func (t *AuthnToken4) ShouldRefresh() bool { 169 return time.Now().After(t.Timestamp.Add(5 * time.Minute)) 170} 171