Server Auth with CCG - initial setup for DI container registration
JanVargovsky opened this issue · comments
- I have checked that the SDK documentation doesn't solve my issue.
- I have checked that the API documentation doesn't solve my issue.
- I have searched the Box Developer Forums and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
- I have searched Issues in this repo and my issue isn't already reported.
Description of the Issue
The code provided in the documentation example is working fine but most modern applications use DI with container registrations that are synchronous. This PR added support for the CCG auth but requires a user token that is available via API call only and this is the root of the issue unless you do the API call to obtain a user token in a synchronous way, there is no way of registering e.g. IBoxClient
into a container, so you have to wrap it into an async factory method.
Another gotcha is the token refresh via event
, even if would wrap IBoxClient
into an async factory method, register a method to the IBoxClient.Auth.SessionAuthenticated
event that handles the token refresh (token caching), do some API calls (logic), I should unregister the method to avoid leaks.
Steps to Reproduce
var boxConfig = new BoxConfigBuilder("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
.Build();
var boxCCG = new BoxCCGAuth(boxConfig);
var userToken = await boxCCG.UserTokenAsync("USER_ID"); //valid for 60 minutes so should be cached and re-used
IBoxClient userClient = boxCCG.UserClient(userToken, "USER_ID");
userClient.Auth.SessionAuthenticated += delegate(object o, SessionAuthenticatedEventArgs e)
{
string newAccessToken = e.Session.AccessToken;
// cache the new access token
};
refactored code with token caching and exposing IBoxClient
via the async factory method
string? userToken = null;
async ValueTask<IBoxClient> CreateBoxClientAsync()
{
userToken ??= await boxCCG.UserTokenAsync(userId); //valid for 60 minutes so should be cached and re-used
var userClient = boxCCG.UserClient(userToken, serviceAccount);
userClient.Auth.SessionAuthenticated += OnSessionAuthenticated;
return userClient;
}
void OnSessionAuthenticated(object sender, SessionAuthenticatedEventArgs e)
{
userToken = e.Session.AccessToken;
}
var userClient = await CreateBoxClientAsync();
// ... do logic ...
// I should still unregister the method, which is too much in my view
userClient.Auth.SessionAuthenticated -= OnSessionAuthenticated;
Expected Behavior
Ability to instantiate IBoxClient
without initial user id => user token
exchange.
Versions Used
.Net SDK: 4.6.0
This makes sense and would make it much easier to create the client synchronously without explicitly calling for token. Just so you know, contributions to this repo are always welcome!
For a bit hacky workaround, you could try passing a "fake" or expired token (not a null or empty one as the format should be a proper one!) to the IBoxClient
instead of calling UserTokenAsync
. The first api call should hit 401 and then sdk should call api once again for a new token.
It seems like I've discovered a bug, if I'm using a service account, it's not able to refresh expired (or fake) token. I wanted to create a new custom app because 2FA doesn't work but it's not a problem, will reach your support for whats wrong. I will try it more in detail and optionally fill a PR then.
General question. We have an integration app and we want to upload files as a service account, so its clearly visible in the web Box UI
is this the way creating a custom app, auth with CCG and pass service account into the SDK?
Does the UserTokenAsync
function return the token in your case? It is also used when refreshing the token. I believe you need the Generate user access tokens
scope from the developer console, and your application must be authorized by an administrator in admin console.
We have an integration app and we want to upload files as a service account, so its clearly visible in the web Box UI is this the way creating a custom app, auth with CCG and pass service account into the SDK?
Yes, the SDK can be used to automate things like uploading files so that you don't have to do it through the Box UI. Files uploaded via the SDK should be visible on the site (when appropriate permissions are given etc.).
Does the
UserTokenAsync
function return the token in your case?
It doesn't. Should I create another issue here or contact your support regarding this? I believe I have everything right, all I need is to make uploads to a folder look like it's been uploaded by an app instead of me as a user.
The setup is the same, I change userId
which is a number
to the serviceAccount
that is automatically generated account/email
I've got all the checkboxes in the app scopes enabled, mainly the Generate user access tokens
var ad = await boxCCG.AdminTokenAsync(); // ok
var t = await boxCCG.UserTokenAsync(userId); // ok
var t2 = await boxCCG.UserTokenAsync(serviceAccount); // invalid_grant
Exception details
message: The API returned an error [BadRequest] invalid_grant - Grant credentials are invalid
stack trace:
at Box.V2.Extensions.BoxResponseExtensions.ParseResults[T](IBoxResponse`1 response, IBoxConverter converter)
at Box.V2.CCGAuth.BoxCCGAuth.<CCGAuthPostAsync>d__8.MoveNext()
at Box.V2.CCGAuth.BoxCCGAuth.<UserTokenAsync>d__7.MoveNext()
at Program.<<Main>$>d__0.MoveNext() in ...\Program.cs:line 22
behaves the same with and without enterprise.
Seems like service account ID is just for the UI - for the invite to a folder
In the code, I needed to use admin token/client and final result looks as expected now.
final code is this:
var boxConfig = new BoxConfigBuilder(clientId, clientSecret)
.SetEnterpriseId("<enterprise id>")
.Build();
var boxCCG = new BoxCCGAuth(boxConfig);
var client = boxCCG.AdminClient("fake");
client.Auth.SessionAuthenticated += OnSessionAuthenticated;
as I'm rereading the documentation, it was my misunderstanding. However, I'd like to create a PR to make the token optional or totally remove it since it can be refreshed internally, which option do you like more?
I'm glad the problem has been resolved! I think the preferred way would be to make the token optional to maintain backwards compatibility if possible and don't change this behavior for existing clients.