I'm attempting to use the Brightspace API, but I'm getting a 403 (Forbidden) response.
I've registered my application using the Manage Extensability (/d2l/lp/extensibility/home) page, I've generated a user ID and key from the API Test Tool.
Using all of this, I've installed the D2L.Extensibility.AuthSdk
NuGet package in my project. Then, in the relevant class, I've created a property for a UserContext and am initializing it in the constructor like this:
_d2LUserContext = new D2LAppContextFactory()
.Create(OrionConfiguration.D2LApplicationId, OrionConfiguration.D2LApiKey)
.CreateUserContext(
"censored user id",
"censored user key",
new HostSpec("https", OrionConfiguration.D2LUrl.Substring(8), 443)
);
Notes:
.Substring(8)
is because D2LUrl includes the URL schemeThen, I am trying to call the API. The code for this is split up in a few methods.
private string AuthParam(string path, string method)
{
return _d2LUserContext
.CreateAuthenticatedTokens($"/d2l/api/lp/1.2{path}", method)
.Select(tuple => $"{tuple.Item1}={tuple.Item2}")
.Aggregate((acc, p) => $"{acc}&{p}");
}
public Task<UserResponse> CreateUser(UserRequest userRequest)
{
const string path = "/users";
return _httpUtils.Post<UserResponse>($"{path}/?{AuthParam(path, "POST")}", userRequest);
}
UserRequest
is a POC#O (Plain Old C# Object) version of the model the API expects.
Here is the relevant method in the HttpUtils
class - this is a wrapper around the HttpClient
I wrote to get rid of some of the boilerplate in other classes.
internal async Task<T> Post<T>(string route, dynamic body)
{
var response = await _httpClient.PostAsync(
_baseUrl + route,
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json")
);
_logger.LogInformation($"POST request to {route}");
_logger.LogInformation(await response.Content.ReadAsStringAsync());
return JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
Now, putting it all together, when I try to debug these methods getting called, I set a breakpoint the line after my POST request, and we can see I get a 403
I'm wondering why this is happening. The user that the key and id were generated from is a super administrator, so this is not a permissions issue.
The actual API route in question has a trailing slash and the trailing slash thus needs putting into the method you have that generates the authentication token/signature. It looks to me like what you're doing is passing a path
that does not have this trailing slash, and then putting it into the request as a side effect of when you tack on the query parameters, so you're generating an authentication signature for one API route and then using another in the call.
Brightspace API routes are quite sensitive to that trailing slash, and unfortunately they're not always applied cleanly or consistently in routes.