Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ jobs:
BW_PROJECT_TEST_NAME: Bandwidth.Iris.Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set release version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v5
with:
dotnet-version: "6.0"

Expand Down
17 changes: 11 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
dotnet: [6.0.x, 7.0.x]
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ matrix.dotnet }}

Expand All @@ -26,10 +26,15 @@ jobs:
DOTNET: ${{ matrix.dotnet }}
run: dotnet test Bandwidth.Iris.Tests

- name: Notify Slack of failures
uses: Bandwidth/build-notify-slack-action@v1.0.0
if: failure() && !github.event.pull_request.draft
notify_for_failures:
name: Notify for Failures
needs: [test]
if: failure()
runs-on: ubuntu-latest
steps:
- name: Notify Slack of Failures
uses: Bandwidth/build-notify-slack-action@v2
with:
job-status: ${{ job.status }}
job-status: failure
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
slack-channel: ${{ secrets.SLACK_CHANNEL }}
146 changes: 146 additions & 0 deletions Bandwidth.Iris.Tests/ClientAuthTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace Bandwidth.Iris.Tests
{
public class ClientAuthTests
{
[Fact]
public async Task UsesBearerHeaderWhenAccessTokenProvided()
{
var token = "abc123";
using (var server = new HttpServer(new RequestHandler
{
EstimatedMethod = "GET",
EstimatedPathAndQuery = "/v1.0/test",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", "Bearer " + token}
}
}))
{
var client = Client.GetInstanceWithAccessToken(Helper.AccountId, token, apiEndpoint: "http://localhost:3001/");
await client.MakeGetRequest("test", null, null, true);
if (server.Error != null) throw server.Error;
}
}

[Fact]
public async Task UsesBearerHeaderWhenValidEvenWithClientCredentials()
{
var clientId = "FakeClientId";
var clientSecret = "FakeClientSecret";
var token = "validToken";
var tokenExpiration = DateTimeOffset.UtcNow.AddHours(1);
var tokenRequestAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId + ":" + clientSecret));

using (var apiServer = new HttpServer(new RequestHandler
{
EstimatedMethod = "GET",
EstimatedPathAndQuery = "/v1.0/test",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", "Bearer " + token}
}
}))
{
var client = Client.GetInstanceWithClientCredentialsAndAccessToken(Helper.AccountId, clientId, clientSecret, token, tokenExpiration, "http://localhost:3001/");
await client.MakeGetRequest("test", null, null, true);
if (apiServer.Error != null) throw apiServer.Error;
}


}

[Fact]
public async Task RefreshesTokenWithinOneMinuteSkew()
{
var clientId = "FakeClientId";
var clientSecret = "FakeClientSecret";
var tokenRequestAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId + ":" + clientSecret));
// Token endpoint server on port 3002: first call returns tk1 (short expiry), second returns tk2
using (var tokenServer = new HttpServer(new[] {
new RequestHandler
{
EstimatedMethod = "POST",
EstimatedPathAndQuery = "/",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", tokenRequestAuthString}
},
ContentToSend = new StringContent("{\"access_token\":\"tk1\",\"expires_in\":30}", Encoding.UTF8, "application/json")
},
new RequestHandler
{
EstimatedMethod = "POST",
EstimatedPathAndQuery = "/",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", tokenRequestAuthString}
},
ContentToSend = new StringContent("{\"access_token\":\"tk2\",\"expires_in\":3600}", Encoding.UTF8, "application/json")
}
}, prefix: "http://localhost:3002/"))
{
// API server expects first GET with tk1, second GET with tk2
using (var apiServer = new HttpServer(new[] {
new RequestHandler
{
EstimatedMethod = "GET",
EstimatedPathAndQuery = "/v1.0/test",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", "Bearer tk1"}
}
},
new RequestHandler
{
EstimatedMethod = "GET",
EstimatedPathAndQuery = "/v1.0/test",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", "Bearer tk2"}
}
}
}))
{
var client = Client.GetInstanceWithClientCredentials(Helper.AccountId, clientId, clientSecret, "http://localhost:3002/");

await client.MakeGetRequest("test", null, null, true);
if (apiServer.Error != null) throw apiServer.Error;

// Second call should see short expiry (<= 1 minute) and refresh to tk2
await Task.Delay(100); // small delay to avoid race conditions
await client.MakeGetRequest("test", null, null, true);
if (apiServer.Error != null) throw apiServer.Error;
}
}
}

[Fact]
public async Task UsesBasicAuthWhenNoOauthSupplied()
{
var userName = "user1";
var password = "pass123";
var basicAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(userName + ":" + password));
using (var server = new HttpServer(new RequestHandler
{
EstimatedMethod = "GET",
EstimatedPathAndQuery = "/v1.0/test",
EstimatedHeaders = new Dictionary<string, string>
{
{"Authorization", basicAuthString}
}
}))
{
var client = Client.GetInstance(Helper.AccountId, userName, password, apiEndpoint: "http://localhost:3001/");
await client.MakeGetRequest("test", null, null, true);
if (server.Error != null) throw server.Error;
}
}
}
}
Loading
Loading