Skip to content

Commit a44b04a

Browse files
authored
SWI-9214 Add OAuth Support (#92)
* SWI-9214 Add OAuth Support * add tests * update workflow files * yaml -> yml * update readme
1 parent f8ce35f commit a44b04a

5 files changed

Lines changed: 328 additions & 15 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ jobs:
1313
BW_PROJECT_TEST_NAME: Bandwidth.Iris.Tests
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717

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

.github/workflows/test.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ jobs:
1313
dotnet: [6.0.x, 7.0.x]
1414
steps:
1515
- name: Checkout repo
16-
uses: actions/checkout@v4
16+
uses: actions/checkout@v6
1717

1818
- name: Setup .NET
19-
uses: actions/setup-dotnet@v4
19+
uses: actions/setup-dotnet@v5
2020
with:
2121
dotnet-version: ${{ matrix.dotnet }}
2222

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

29-
- name: Notify Slack of failures
30-
uses: Bandwidth/build-notify-slack-action@v1.0.0
31-
if: failure() && !github.event.pull_request.draft
29+
notify_for_failures:
30+
name: Notify for Failures
31+
needs: [test]
32+
if: failure()
33+
runs-on: ubuntu-latest
34+
steps:
35+
- name: Notify Slack of Failures
36+
uses: Bandwidth/build-notify-slack-action@v2
3237
with:
33-
job-status: ${{ job.status }}
38+
job-status: failure
3439
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
3540
slack-channel: ${{ secrets.SLACK_CHANNEL }}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Net.Http;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
8+
namespace Bandwidth.Iris.Tests
9+
{
10+
public class ClientAuthTests
11+
{
12+
[Fact]
13+
public async Task UsesBearerHeaderWhenAccessTokenProvided()
14+
{
15+
var token = "abc123";
16+
using (var server = new HttpServer(new RequestHandler
17+
{
18+
EstimatedMethod = "GET",
19+
EstimatedPathAndQuery = "/v1.0/test",
20+
EstimatedHeaders = new Dictionary<string, string>
21+
{
22+
{"Authorization", "Bearer " + token}
23+
}
24+
}))
25+
{
26+
var client = Client.GetInstanceWithAccessToken(Helper.AccountId, token, apiEndpoint: "http://localhost:3001/");
27+
await client.MakeGetRequest("test", null, null, true);
28+
if (server.Error != null) throw server.Error;
29+
}
30+
}
31+
32+
[Fact]
33+
public async Task UsesBearerHeaderWhenValidEvenWithClientCredentials()
34+
{
35+
var clientId = "FakeClientId";
36+
var clientSecret = "FakeClientSecret";
37+
var token = "validToken";
38+
var tokenExpiration = DateTimeOffset.UtcNow.AddHours(1);
39+
var tokenRequestAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId + ":" + clientSecret));
40+
41+
using (var apiServer = new HttpServer(new RequestHandler
42+
{
43+
EstimatedMethod = "GET",
44+
EstimatedPathAndQuery = "/v1.0/test",
45+
EstimatedHeaders = new Dictionary<string, string>
46+
{
47+
{"Authorization", "Bearer " + token}
48+
}
49+
}))
50+
{
51+
var client = Client.GetInstanceWithClientCredentialsAndAccessToken(Helper.AccountId, clientId, clientSecret, token, tokenExpiration, "http://localhost:3001/");
52+
await client.MakeGetRequest("test", null, null, true);
53+
if (apiServer.Error != null) throw apiServer.Error;
54+
}
55+
56+
57+
}
58+
59+
[Fact]
60+
public async Task RefreshesTokenWithinOneMinuteSkew()
61+
{
62+
var clientId = "FakeClientId";
63+
var clientSecret = "FakeClientSecret";
64+
var tokenRequestAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId + ":" + clientSecret));
65+
// Token endpoint server on port 3002: first call returns tk1 (short expiry), second returns tk2
66+
using (var tokenServer = new HttpServer(new[] {
67+
new RequestHandler
68+
{
69+
EstimatedMethod = "POST",
70+
EstimatedPathAndQuery = "/",
71+
EstimatedHeaders = new Dictionary<string, string>
72+
{
73+
{"Authorization", tokenRequestAuthString}
74+
},
75+
ContentToSend = new StringContent("{\"access_token\":\"tk1\",\"expires_in\":30}", Encoding.UTF8, "application/json")
76+
},
77+
new RequestHandler
78+
{
79+
EstimatedMethod = "POST",
80+
EstimatedPathAndQuery = "/",
81+
EstimatedHeaders = new Dictionary<string, string>
82+
{
83+
{"Authorization", tokenRequestAuthString}
84+
},
85+
ContentToSend = new StringContent("{\"access_token\":\"tk2\",\"expires_in\":3600}", Encoding.UTF8, "application/json")
86+
}
87+
}, prefix: "http://localhost:3002/"))
88+
{
89+
// API server expects first GET with tk1, second GET with tk2
90+
using (var apiServer = new HttpServer(new[] {
91+
new RequestHandler
92+
{
93+
EstimatedMethod = "GET",
94+
EstimatedPathAndQuery = "/v1.0/test",
95+
EstimatedHeaders = new Dictionary<string, string>
96+
{
97+
{"Authorization", "Bearer tk1"}
98+
}
99+
},
100+
new RequestHandler
101+
{
102+
EstimatedMethod = "GET",
103+
EstimatedPathAndQuery = "/v1.0/test",
104+
EstimatedHeaders = new Dictionary<string, string>
105+
{
106+
{"Authorization", "Bearer tk2"}
107+
}
108+
}
109+
}))
110+
{
111+
var client = Client.GetInstanceWithClientCredentials(Helper.AccountId, clientId, clientSecret, "http://localhost:3002/");
112+
113+
await client.MakeGetRequest("test", null, null, true);
114+
if (apiServer.Error != null) throw apiServer.Error;
115+
116+
// Second call should see short expiry (<= 1 minute) and refresh to tk2
117+
await Task.Delay(100); // small delay to avoid race conditions
118+
await client.MakeGetRequest("test", null, null, true);
119+
if (apiServer.Error != null) throw apiServer.Error;
120+
}
121+
}
122+
}
123+
124+
[Fact]
125+
public async Task UsesBasicAuthWhenNoOauthSupplied()
126+
{
127+
var userName = "user1";
128+
var password = "pass123";
129+
var basicAuthString = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(userName + ":" + password));
130+
using (var server = new HttpServer(new RequestHandler
131+
{
132+
EstimatedMethod = "GET",
133+
EstimatedPathAndQuery = "/v1.0/test",
134+
EstimatedHeaders = new Dictionary<string, string>
135+
{
136+
{"Authorization", basicAuthString}
137+
}
138+
}))
139+
{
140+
var client = Client.GetInstance(Helper.AccountId, userName, password, apiEndpoint: "http://localhost:3001/");
141+
await client.MakeGetRequest("test", null, null, true);
142+
if (server.Error != null) throw server.Error;
143+
}
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)