Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add region to access token #546

Merged
merged 4 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/Twilio/JWT/AccessToken/Token.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Token : BaseJwt
private readonly string _identity;
private readonly DateTime? _nbf;
private readonly HashSet<IGrant> _grants;
private readonly string _region;

/// <summary>
/// Create a new Access Token
Expand All @@ -24,14 +25,16 @@ public class Token : BaseJwt
/// <param name="expiration">Token expiration</param>
/// <param name="nbf">Token nbf</param>
/// <param name="grants">Token grants</param>
/// <param name="region">Token region</param>
public Token(
string accountSid,
string signingKeySid,
string secret,
string identity = null,
DateTime? expiration = null,
DateTime? nbf = null,
HashSet<IGrant> grants = null
HashSet<IGrant> grants = null,
string region = null
) : base(secret, signingKeySid, expiration.HasValue ? expiration.Value : DateTime.UtcNow.AddSeconds(3600))
{
var now = BaseJwt.ConvertToUnixTimestamp(DateTime.UtcNow);
Expand All @@ -40,6 +43,7 @@ public Token(
this._identity = identity;
this._nbf = nbf;
this._grants = grants;
this._region = region;
}

/// <summary>
Expand Down Expand Up @@ -75,14 +79,32 @@ public override DateTime? Nbf
}
}

/// <summary>
/// The region associated with this account
/// </summary>
public string Region
{
get
{
return _region;
}
}

/// <summary>
/// Headers for an Access Token
/// </summary>
public override Dictionary<string, object> Headers
{
get
{
return new Dictionary<string, object> { { "cty", "twilio-fpa;v=1" } };
var headers = new Dictionary<string, object> { { "cty", "twilio-fpa;v=1" } };

if (_region != null)
{
headers.Add("twr", _region);
}

return headers;
}
}

Expand Down
49 changes: 39 additions & 10 deletions test/Twilio.Test/Jwt/AccessToken/AccessTokenTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Twilio.Jwt;
Expand All @@ -16,16 +17,9 @@ public TestToken(
string identity = null,
DateTime? expiration = null,
DateTime? nbf = null,
HashSet<IGrant> grants = null
) : base(accountSid, signingKeySid, secret, identity, expiration, nbf, grants) {}

public override Dictionary<string, object> Headers
{
get
{
return new Dictionary<string, object>();
}
}
HashSet<IGrant> grants = null,
string region = null
) : base(accountSid, signingKeySid, secret, identity, expiration, nbf, grants, region) {}
}

[TestFixture]
Expand Down Expand Up @@ -54,6 +48,41 @@ public void TestBuildToken()
Assert.AreEqual("{}", payload["grants"].ToString());
}

[Test]
public void TestHaveRegion()
{
var now = DateTime.UtcNow;
var token = new TestToken("AC456", "SK123", Secret, region: "foo").ToJwt();
Assert.IsNotNull(token);
Assert.IsNotEmpty(token);

var decoded = new DecodedJwt(token, Secret);
var header = decoded.Header;
Assert.IsNotNull(header);
Assert.AreEqual("twilio-fpa;v=1", header["cty"]);
Assert.AreEqual("foo", header["twr"]);
}

[Test]
public void TestEmptyRegion()
{
var now = DateTime.UtcNow;
var token = new TestToken("AC456", "SK123", Secret).ToJwt();
Assert.IsNotNull(token);
Assert.IsNotEmpty(token);

var decoded = new DecodedJwt(token, Secret);
var header = decoded.Header;
Assert.IsNotNull(header);
Assert.AreEqual("twilio-fpa;v=1", header["cty"]);

try {
var twr = header["twr"];
Assert.Fail();
} catch (KeyNotFoundException) {
// Pass
}
}

[Test]
public void TestHaveNbf()
Expand Down
62 changes: 42 additions & 20 deletions test/Twilio.Test/Jwt/DecodedJwt.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,72 @@
#if !NET35
using System.IdentityModel.Tokens.Jwt;
#else
using System;
using System.Collections.Generic;
#endif

using System;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;

namespace Twilio.Tests.Jwt
{
class DecodedJwt
{
#if !NET35
private static byte[] Base64UrlDecode(string input) => Convert.FromBase64String(UrlDecode(input));

public JwtPayload Payload
private static string UrlDecode(string input)
{
get
var output = input;
output = output.Replace('-', '+'); // 62nd char of encoding
output = output.Replace('_', '/'); // 63rd char of encoding

// Pad with trailing '='s
switch (output.Length % 4)
{
return token.Payload;
case 0:
break; // No pad chars in this case
case 2:
output += "==";
break; // Two pad chars
case 3:
output += "=";
break; // One pad char
default:
throw new Exception($"Illegal base-64 string: '{input}'.");
}

return output;
}

private readonly JwtSecurityToken token;
private static IDictionary<string, object> DecodeJwtPart(string part)
{
var bytes = Base64UrlDecode(part);
var json = Encoding.UTF8.GetString(bytes);
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

public DecodedJwt(string jwt, string secret)
return data;
}

public IDictionary<string, object> Header
{
token = new JwtSecurityToken(jwt);
get
{
return _header;
}
}

#else
public IDictionary<string, object> Payload
{
get
{
return JsonConvert.DeserializeObject<Dictionary<string, object>>(token);
return _payload;
}
}

private readonly string token;
private readonly IDictionary<string, object> _header;
private readonly IDictionary<string, object> _payload;

public DecodedJwt(string jwt, string secret)
{
token = JWT.JsonWebToken.Decode(jwt, secret);
var parts = jwt.Split('.');
_header = DecodeJwtPart(parts[0]);
_payload = DecodeJwtPart(parts[1]);
}

#endif

}
}