I’m working on a WPF project using .NET Core 6.0 in Visual Studio 2022. I need to programmatically generate boards through the Miro REST API but keep running into authorization issues.
I have my application credentials (AppID and AppSecret) set up correctly for OAuth. I’m using HttpClient library to make the requests. The authentication step seems to work fine, but when I try to actually create a board using the v1/boards endpoint, I get a 401 Unauthorized response.
Here’s my current implementation:
public string MiroSettingsUrl = "https://miro.com/app/settings/user-profile/apps";
public string AppID = "myappid";
public string AppSecret = "myappsecret";
internal async void GenerateMiroBoard()
{
try
{
var authClient = new HttpClient();
var authUrl = $"https://miro.com/app-install/?response_type=code&client_id={AppID}";
var authRequest = new HttpRequestMessage(HttpMethod.Post, authUrl);
authRequest.Headers.Add("Authorization", "Bearer " + AppSecret);
var authResponse = await authClient.SendAsync(authRequest);
var authResult = authResponse; //Returns OK status
var boardClient = new HttpClient();
var boardRequest = new HttpRequestMessage(HttpMethod.Post, "https://api.miro.com/v1/boards");
boardRequest.Headers.Add("Accept", "application/json");
boardRequest.Content = new StringContent(
"{\"name\":\"My New Board\",\"sharingPolicy\":{\"access\":\"private\",\"teamAccess\":\"private\"}}",
System.Text.Encoding.UTF8,
"application/json"
);
var boardResponse = await boardClient.SendAsync(boardRequest);
var boardResult = boardResponse; //Returns Unauthorized
}
catch(Exception ex)
{
var errorMsg = ex.Message;
}
}
What’s the correct way to handle the OAuth flow and pass the access token for board creation? Am I missing something in the authorization headers?
you’re not finishing the oauth flow. that auth step doesn’t give you an access token - you need to handle the callback url and swap the auth code for a real token first. then use that token in your board request headers, not the app secret.
Your problem is you’re skipping the OAuth flow entirely. That app-install URL is for users authorizing in a browser, not for making API calls directly. After the user authorizes your app, grab the authorization code from the callback and swap it for an access token at the token endpoint. Then use that token in your board creation request with Authorization: Bearer {your_access_token} in the headers. Don’t use the app secret for API calls - that’s only for the token exchange. You’re also missing the authorization header completely in your board request, leading to the 401 error.
You’re mixing up the OAuth flow steps. Don’t send a POST request to the app-install URL - that’s for browser redirects, not direct API calls. Here’s what you need to do: redirect users to the authorization URL in their browser, handle the callback to get the authorization code, then POST to https://api.miro.com/v1/oauth/token with your client credentials and code to get the access token. Use that token in your board creation request as Authorization: Bearer {access_token}. Never use the app secret directly for API requests - only during token exchange. Also, switch to the v2 API since v1 is deprecated. The v2 boards endpoint has much better C# docs and examples.
The Problem: The user is encountering 401 Unauthorized errors when trying to create a Miro board using the Miro REST API and .NET Core 6.0. Their current implementation attempts to authenticate using the app secret directly in the board creation request, which is incorrect. The OAuth flow is not properly implemented, resulting in the missing access token needed for authorization.
Understanding the “Why” (The Root Cause): The Miro API uses OAuth 2.0 for authentication. This requires a multi-step process: the application first obtains an authorization code by redirecting the user to Miro’s authorization URL. Then, the application exchanges this code for an access token using the client credentials (App ID and App Secret). Finally, the access token is included in the headers of subsequent API requests to authorize the action. The original code attempts to use the app secret directly, which is an insecure practice and bypasses the necessary user authorization step. Directly using the App Secret for API calls is not how the Miro API works and should be avoided.
Step-by-Step Guide:
-
Implement the Complete OAuth 2.0 Flow: The core issue is the incomplete OAuth flow. Instead of trying to directly POST to https://miro.com/app-install/, you need to guide the user through a browser-based authorization process to obtain the required access token. This involves several steps:
-
a) Redirect to the Authorization URL: Use a WebView2 control (or similar technology for launching the browser) within your WPF application to redirect the user to the Miro authorization URL. This URL will include your App ID and a redirect URI that points back to your application. The exact URL structure will be found in the Miro API documentation.
-
b) Handle the Callback: When the user authorizes your application in Miro, Miro will redirect them to your specified redirect URI, including an authorization code in the URL parameters. Your application must capture this code.
-
c) Exchange the Code for an Access Token: Make a POST request to Miro’s token endpoint (https://api.miro.com/v1/oauth/token) with your App ID, App Secret, and the authorization code obtained in step (b). The response will contain an access token.
-
d) Use the Access Token for Board Creation: Now, include the access token in the Authorization header of your board creation request using the Bearer scheme. The request should look like this:
var boardClient = new HttpClient();
var boardRequest = new HttpRequestMessage(HttpMethod.Post, "https://api.miro.com/v1/boards");
boardRequest.Headers.Add("Authorization", $"Bearer {accessToken}");
boardRequest.Headers.Add("Accept", "application/json");
boardRequest.Content = new StringContent(
"{\"name\":\"My New Board\",\"sharingPolicy\":{\"access\":\"private\",\"teamAccess\":\"private\"}}",
System.Text.Encoding.UTF8,
"application/json"
);
var boardResponse = await boardClient.SendAsync(boardRequest);
// Handle the response
-
Consider Using a Library: Managing OAuth flows can be complex. Consider using a well-maintained OAuth 2.0 client library for C# to simplify the process. These libraries handle many of the low-level details, such as token refreshing.
Common Pitfalls & What to Check Next:
- Incorrect Redirect URI: Ensure that the redirect URI you specified during the registration of your Miro application matches the URI your WPF application is listening on. A mismatch will prevent the authorization code from being returned correctly.
- Token Expiration: Access tokens have a limited lifespan. Implement token refreshing to ensure your application can continue to make API calls after the token expires. Consult the Miro API documentation for details on how to obtain a refresh token and use it to get new access tokens.
- Error Handling: Implement robust error handling to catch potential issues during each step of the OAuth flow and the board creation process. Log all errors for debugging purposes.
- API Version: While the code uses v1, consider upgrading to v2 of the Miro API for better support and documentation.
Still running into issues? Share your (sanitized) code snippet showing your OAuth implementation, the Miro API responses you’re receiving, and any error messages you encounter. The community is here to help!
you’re not actually using the access token - that’s the problem. after the oauth redirect, you get an auth code. then you need to exchange it for an access token by posting to /v1/oauth/token. your board request should have authorization: bearer {actual_token} in the header, not the app secret. also, ditch async void and use async task instead.
Had this same problem last year when connecting Miro to our project management system. You’re trying to use OAuth like it’s basic API auth, but it needs user interaction. Open the authorization URL in a browser control inside your WPF app or just launch the default browser. Then catch the callback URL after the user authorizes - that callback has the authorization code you’ll swap for an access token with a POST to the token endpoint. Save that token and add it as Authorization: Bearer {token} when creating boards. Access tokens expire, so don’t forget refresh token logic. I used WebView2 in WPF to keep everything smooth within the app.
This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.