diff --git a/E2ETests/E2ETests.csproj b/E2ETests/E2ETests.csproj
index f6f674dd8e22fe2496e7b98588853ee0ef6fe2c8..5c22f888dff9e67e999ba2f5716fec2c0fedc16f 100644
--- a/E2ETests/E2ETests.csproj
+++ b/E2ETests/E2ETests.csproj
@@ -15,4 +15,8 @@
         <PackageReference Include="coverlet.collector" Version="3.1.0" />
     </ItemGroup>
 
+    <ItemGroup>
+      <ProjectReference Include="..\FitConnect\FitConnect.csproj" />
+    </ItemGroup>
+
 </Project>
diff --git a/E2ETests/SenderTest.cs b/E2ETests/SenderTest.cs
index fc1e9c8509226050246d6a0294fd199bdb2a431e..4482f720dc4e4f63a017ee98a5d7053030aee1c8 100644
--- a/E2ETests/SenderTest.cs
+++ b/E2ETests/SenderTest.cs
@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using FitConnect;
 using FluentAssertions;
 using Newtonsoft.Json;
 using NUnit.Framework;
@@ -9,6 +10,7 @@ namespace E2ETests;
 public class SenderTest {
     private string _clientId = "";
     private string _clientSecret = "";
+    private Sender _sender;
 
     [OneTimeSetUp]
     public void OneTimeSetup() {
@@ -37,6 +39,8 @@ public class SenderTest {
 
     [SetUp]
     public void Setup() {
+        _sender = new Sender(null,
+            FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development));
     }
 
     [Test]
@@ -44,4 +48,11 @@ public class SenderTest {
         _clientId.Should().NotBe("00000000-0000-0000-0000-000000000000");
         _clientSecret.Should().NotBe("0000000000000000000000000000000000000000000");
     }
+
+    [Test]
+    public void GetAccessToken() {
+        var token = _sender.GetTokenAsync(_clientId, _clientSecret).Result;
+        token.Should().NotBeNull();
+        token.ExpiresIn.Should().Be(1800);
+    }
 }
diff --git a/FitConnect/FunctionalBaseClass.cs b/FitConnect/FunctionalBaseClass.cs
index 13ecf7df33aed4329d5e3ed98c2305d1ee44105e..9b7ba4c6577ddb267233644f574f3717bade274e 100644
--- a/FitConnect/FunctionalBaseClass.cs
+++ b/FitConnect/FunctionalBaseClass.cs
@@ -1,5 +1,7 @@
+using System.Net.Http.Headers;
 using System.Net.Http.Json;
 using System.Text;
+using FitConnect.Models;
 using Microsoft.Extensions.Logging;
 
 namespace FitConnect;
@@ -85,6 +87,26 @@ public class FunctionalBaseClass {
     }
 
 
+    public async Task<OAuthAccessToken?> GetTokenAsync(string clientId, string clientSecret) {
+        var client = new HttpClient();
+        client.DefaultRequestHeaders.Accept.Add(
+            MediaTypeWithQualityHeaderValue.Parse("application/json"));
+        var content = new FormUrlEncodedContent(new[] {
+            new KeyValuePair<string, string>("grant_type", "client_credentials"),
+            new KeyValuePair<string, string>("client_id", clientId),
+            new KeyValuePair<string, string>("client_secret", clientSecret)
+        });
+
+        var request = new HttpRequestMessage(HttpMethod.Post, Endpoints.TokenUrl) {
+            Content = content,
+            Method = HttpMethod.Post
+        };
+
+        var response = await client.SendAsync(request);
+
+        return await response.Content.ReadFromJsonAsync<OAuthAccessToken>();
+    }
+
     private async Task<T?> RestCall<T>(Uri uri, HttpMethod method, string body) {
         var client = new HttpClient();
         client.DefaultRequestHeaders.Add("Accept", "application/json");
diff --git a/FitConnect/Models/OAuthAccessToken.cs b/FitConnect/Models/OAuthAccessToken.cs
new file mode 100644
index 0000000000000000000000000000000000000000..87ac3872e52a9a9e88e1df33eace09bb8fbd3ed1
--- /dev/null
+++ b/FitConnect/Models/OAuthAccessToken.cs
@@ -0,0 +1,17 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Models;
+
+public class OAuthAccessToken {
+    [JsonPropertyName("access_token")]
+    public string AccessToken { get; set; }
+
+    [JsonPropertyName("scope")]
+    public string Scope { get; set; }
+
+    [JsonPropertyName("token_type")]
+    public string TokenType { get; set; }
+
+    [JsonPropertyName("expires_in")]
+    public int ExpiresIn { get; set; }
+}