-
Notifications
You must be signed in to change notification settings - Fork 305
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: Public OAuth uptake for C# libraries #762
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4ca2eed
Public OAuth uptake for C# libraries
AsabuHere 3486d1a
Public OAuth uptake for C# libraries
AsabuHere 0ec6f95
Public OAuth uptake for C# libraries
AsabuHere e38ff14
Improving the hash code function
AsabuHere 006d4db
Merge branch 'main' into csharp_public_oauth_asabu
AsabuHere File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System; | ||
|
||
namespace Twilio.Annotations | ||
{ | ||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] | ||
public class Deprecated : Attribute | ||
{ | ||
public string Message { get; } | ||
|
||
public Deprecated(string message = "This feature is deprecated") | ||
{ | ||
Message = message; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Twilio.AuthStrategies | ||
{ | ||
public abstract class AuthStrategy | ||
{ | ||
protected AuthStrategy(){} | ||
|
||
public abstract string GetAuthString(); | ||
|
||
public abstract bool RequiresAuthentication(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#if NET35 | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Web.Script.Serialization; | ||
using Twilio.Annotations; | ||
|
||
namespace Twilio.AuthStrategies{ | ||
|
||
[Beta] | ||
public abstract class Base64UrlEncode | ||
{ | ||
public static string Decode(string base64Url) | ||
{ | ||
// Replace URL-safe characters with Base64 characters | ||
string base64 = base64Url | ||
.Replace('-', '+') | ||
.Replace('_', '/'); | ||
|
||
// Add padding if necessary | ||
switch (base64.Length % 4) | ||
{ | ||
case 2: base64 += "=="; break; | ||
case 3: base64 += "="; break; | ||
} | ||
|
||
byte[] bytes = Convert.FromBase64String(base64); | ||
return Encoding.UTF8.GetString(bytes); | ||
} | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System; | ||
using System.Text; | ||
|
||
namespace Twilio.AuthStrategies | ||
{ | ||
public class BasicAuthStrategy : AuthStrategy | ||
{ | ||
private string username; | ||
private string password; | ||
|
||
public BasicAuthStrategy(string username, string password) | ||
{ | ||
this.username = username; | ||
this.password = password; | ||
} | ||
|
||
public override string GetAuthString() | ||
{ | ||
var credentials = username + ":" + password; | ||
var encoded = System.Text.Encoding.UTF8.GetBytes(credentials); | ||
var finalEncoded = Convert.ToBase64String(encoded); | ||
return $"Basic {finalEncoded}"; | ||
} | ||
|
||
public override bool RequiresAuthentication() | ||
{ | ||
return true; | ||
} | ||
|
||
public override bool Equals(object obj) | ||
{ | ||
if (ReferenceEquals(this, obj)) return true; | ||
if (obj == null || GetType() != obj.GetType()) return false; | ||
var that = (BasicAuthStrategy)obj; | ||
return username == that.username && password == that.password; | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
int hash = 17; | ||
hash = hash * 31 + (username != null ? username.GetHashCode() : 0); | ||
hash = hash * 31 + (password != null ? password.GetHashCode() : 0); | ||
return hash; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace Twilio.AuthStrategies | ||
{ | ||
public class NoAuthStrategy : AuthStrategy | ||
{ | ||
public NoAuthStrategy(){} | ||
|
||
public override string GetAuthString() | ||
{ | ||
return string.Empty; | ||
} | ||
|
||
public override bool RequiresAuthentication() | ||
{ | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
using System; | ||
using System.Threading; | ||
using Twilio.Http.BearerToken; | ||
using Twilio.Exceptions; | ||
|
||
#if !NET35 | ||
using System.IdentityModel.Tokens.Jwt; | ||
using System.Threading.Tasks; | ||
#endif | ||
|
||
#if NET35 | ||
using Twilio.Http.Net35; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Web.Script.Serialization; | ||
#endif | ||
|
||
namespace Twilio.AuthStrategies | ||
{ | ||
public class TokenAuthStrategy : AuthStrategy | ||
{ | ||
private string token; | ||
private TokenManager tokenManager; | ||
|
||
|
||
public TokenAuthStrategy(TokenManager tokenManager) | ||
{ | ||
this.tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); | ||
} | ||
|
||
public override string GetAuthString() | ||
{ | ||
FetchToken(); | ||
return $"Bearer {token}"; | ||
} | ||
|
||
public override bool RequiresAuthentication() | ||
{ | ||
return true; | ||
} | ||
|
||
// Token-specific refresh logic | ||
private void FetchToken() | ||
{ | ||
if (string.IsNullOrEmpty(token) || tokenExpired(token)) | ||
{ | ||
lock (typeof(TokenAuthStrategy)) | ||
{ | ||
if (string.IsNullOrEmpty(token) || tokenExpired(token)) | ||
{ | ||
token = tokenManager.fetchAccessToken(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
public override bool Equals(object obj) | ||
{ | ||
if (ReferenceEquals(this, obj)) return true; | ||
if (obj == null || GetType() != obj.GetType()) return false; | ||
var that = (TokenAuthStrategy)obj; | ||
return token == that.token && tokenManager.Equals(that.tokenManager); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
int hash = 17; | ||
hash = hash * 31 + (token != null ? token.GetHashCode() : 0); | ||
hash = hash * 31 + (tokenManager != null ? tokenManager.GetHashCode() : 0); | ||
return hash; | ||
} | ||
|
||
|
||
public bool tokenExpired(String accessToken){ | ||
#if NET35 | ||
return IsTokenExpired(accessToken); | ||
#else | ||
return isTokenExpired(accessToken); | ||
#endif | ||
} | ||
|
||
#if NET35 | ||
public static bool IsTokenExpired(string token) | ||
{ | ||
try | ||
{ | ||
// Split the token into its components | ||
var parts = token.Split('.'); | ||
if (parts.Length != 3) | ||
throw new ArgumentException("Malformed token received"); | ||
|
||
// Decode the payload (the second part of the JWT) | ||
string payload = Base64UrlEncode.Decode(parts[1]); | ||
|
||
// Parse the payload JSON | ||
var serializer = new JavaScriptSerializer(); | ||
var payloadData = serializer.Deserialize<Dictionary<string, object>>(payload); | ||
|
||
// Check the 'exp' claim | ||
if (payloadData.TryGetValue("exp", out object expObj)) | ||
{ | ||
if (long.TryParse(expObj.ToString(), out long exp)) | ||
{ | ||
DateTime expirationDate = UnixTimeStampToDateTime(exp); | ||
return DateTime.UtcNow > expirationDate; | ||
} | ||
} | ||
|
||
// If 'exp' claim is missing or not a valid timestamp, consider the token expired | ||
throw new ApiConnectionException("token expired"); | ||
return true; | ||
Check warning on line 111 in src/Twilio/AuthStrategies/TokenAuthStrategy.cs
|
||
} | ||
catch (Exception ex) | ||
{ | ||
// Handle exceptions (e.g., malformed token or invalid JSON) | ||
Console.WriteLine($"Error checking token expiration: {ex.Message}"); | ||
throw new ApiConnectionException("token expired"); | ||
return true; // Consider as expired if there's an error | ||
Check warning on line 118 in src/Twilio/AuthStrategies/TokenAuthStrategy.cs
|
||
} | ||
} | ||
|
||
private static DateTime UnixTimeStampToDateTime(long unixTimeStamp) | ||
{ | ||
// Unix timestamp is seconds past epoch | ||
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | ||
return epoch.AddSeconds(unixTimeStamp); | ||
} | ||
#endif | ||
|
||
#if !NET35 | ||
public bool isTokenExpired(string token){ | ||
var handler = new JwtSecurityTokenHandler(); | ||
try{ | ||
var jwtToken = handler.ReadJwtToken(token); | ||
var exp = jwtToken.Payload.Exp; | ||
if (exp.HasValue) | ||
{ | ||
var expirationDate = DateTimeOffset.FromUnixTimeSeconds(exp.Value).UtcDateTime; | ||
return DateTime.UtcNow > expirationDate; | ||
} | ||
else | ||
{ | ||
return true; // Assuming token is expired if exp claim is missing | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine($"Error reading token: {ex.Message}"); | ||
|
||
return true; // Treat as expired if there is an error | ||
} | ||
} | ||
#endif | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any function that works in both old and new framework? instead of making two different functions, if we can use single one to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we cannot use single function to do it, whatever works in 3.5 is deprecated in later versions and vice versa