LogoLogo
HomePricingDocumentation
  • 💿Getting Started
    • Installation and Project Setup
    • Hello Perigee!
    • Perigee Application Design
    • Hello Configuration
    • Hello Logs
    • Hello Integration
    • Troubleshooting
    • Case Studies
  • 📃License + Notice
    • 📂Licensing
    • Notice of Third Party Agreements
  • 🚀Perigee and Beyond
    • Extending - Threads
    • Extending - Loaders
    • ⏳All about CRON
  • 🔮API Generation
    • What is API Generation?
    • API Builder
  • 🗺️Architecting YOUR App
    • Design and Requirements
    • Define Sources
    • Requirements
  • 🧩Core Modules
    • 🌐PerigeeApplication
    • 🪡Thread Registry
    • Event Sources
      • Scheduled/Logic
        • CRON Thread
        • Scheduler
        • Sync Agent
      • Watchers
        • SalesForce
        • Sharepoint
        • Directory Watch
        • Directory Notifier
        • IMAP
    • Credential Management
      • Connection Strings
      • Custom Refresh Logic
      • RestSharp Authenticator
      • Credential Store SDK
      • ⁉️Troubleshooting Credentials
    • Integration Utilities
      • HTTP(S) - RestSharp
      • Transaction Coordinator
      • Limiter
      • Watermarking
    • Alert Managers
      • SMS
      • Email
      • Discord
      • Teams
    • File Formats
      • Excel
      • CSV
    • 📁File System Storage
      • File Revision Store
      • Concurrent File Store
      • FileSync + Cache
    • Third Party
      • SmartSheets
      • Microsoft Graph
    • Perigee In Parallel
      • Parallel Processing Reference
      • Extensions
      • GroupProcessor
      • SingleProcessor
    • 🧱Utility Classes
      • Metrics
      • F(x) Expressions
      • Multi-Threaded Processor (Scatter Gather)
      • OpenAI - GPT
      • XML Converter
      • Dynamic Data Table
      • Debounce
      • Thread Conditions
      • Perigee Utility Class
      • Network Utility
      • Lists
      • FileUtil
      • Inclusive2DRange
      • Strings, Numbers, Dates
      • Nested Sets
      • Behavior Trees
      • JsonCompress
      • Topological Sorting
      • DBDownloader
    • 🈁Bit Serializer
  • 📣Examples and Demos
    • API + Perigee
    • 📰Excel Quick Load
    • SalesForce Watcher
    • Report Scheduler
    • Agent Data Synchronization
    • 📩IMAP Echo bot
    • Watch and load CSVs
    • Graph Delegated Authorization + DataVerse
    • Coordinator Demo
    • Azure Service Bus
    • QuickBooks Online
  • 📘Blueprints
    • Perigee With .NET Hosting
    • Web Host Utilities
    • 🔌Plugin Load Context
  • 🎞️Transforms
    • 🌟What is Transforms?
    • 📘Terminology
    • 🦾The Mapping Document
    • 👾Transformation Process
    • 😎Profile
    • 🎒Automation
      • 🕓Package Options
      • 🔳Configuration
    • 🔧Utilities
      • 🧹Clean
      • 📑Map File
      • 🔎File Identification
      • 🗺️Map Generation
      • 🪅Insert Statement Generation
  • 🗃️Transform SDK
    • 👋Quick Start Guide
    • 🥳MapTo
    • 🔌Authoring Plugins
      • 🔘File IO Process
      • 📢Data Quality
      • 🟢Transform Process
    • SDK Reference
      • 🔘FileIOProcessData
      • 📢DataQualityContext
      • 🎛️TransformDataContext
      • 🏅TransformResult
Powered by GitBook
On this page
  • QuickBooks Online x Perigee
  • The Core of the Process
  • Re-Register the Callback on Application Startup 🔄
  • Making Authorized Realm Calls Is Simpler Than Ever
Export as PDF
  1. Examples and Demos

QuickBooks Online

QuickBooks Online x Perigee

Working with OAuth Flow can be a double-edged sword. While it's crucial for securely accessing APIs, managing tokens, authorization state, and timeouts can be challenging. Get it wrong, and you may find yourself locked out of a resource or stuck with an expired token during an important operation.

QuickBooks Online (QBO) offers a comprehensive guide and an even better playground for developers, especially those just starting with OAuth. But, here comes the tricky part: you have to manage independent sets of authorization and refresh tokens for each authorized company (known as a realm) within your application.

This article breaks down how to use Perigee to effortlessly manage these tokens across multiple realms.

The Core of the Process

The following C# code snippet is executed immediately after receiving the RealmID and Code parameters from the API callback:

//Reach out to: https://developer.api.intuit.com/.well-known/openid_configuration
// Get the token endpoint
ThreadRegistry reg = ThreadRegistry.Instance;
string TokenEndpoint = "";

//Create a client pointing to the QBO Token Endpoint
using var authClient = new RestClient(new RestClientOptions(TokenEndpoint)
{
    Authenticator = new RestSharp.Authenticators.HttpBasicAuthenticator(
    reg.GetValue<string>("AppSettings:client_id")!,
    reg.GetValue<string>("AppSettings:client_secret")!)
});
var authReq = new RestRequest("", Method.Post);


//Add parameters
authReq.AddParameter("redirect_uri", reg.GetValue<string>("AppSettings:redirect"));
authReq.AddParameter("grant_type", "authorization_code");
authReq.AddParameter("code", code);

//Execute - Getting back an initial authorization code and refresh code. 
var rsp = authClient.ExecuteRetry<QBAuth.Tokens>(authReq, 2);

if (rsp.IsSuccessful)
{
    //Each credential is assigned a name: QBA- + realmID
    string qbaName = $"QBA-{realmId}";

    //Step 1) Persist an initial credential we received from the token callback
    CredentialStore.PersistCredential(
    new RestSharpCredentialStoreItem(new RestSharp.Authenticators.JwtAuthenticator(rsp.Data?.AccessToken ?? ""), DateTimeOffset.UtcNow.AddSeconds(rsp.Data?.ExpiresIn ?? 3000))
    {
        RefreshToken = rsp.Data?.RefreshToken,
        StoreA = rsp.Data?.IdToken,
        Environment = realmId,
        Name = qbaName
    });

    //Step 2) Register realm refresh for future usage on expiration
    CredentialStore.RegisterRefresh(qbaName, (o) =>
    {
        using var authClient = new RestClient(new RestClientOptions(TokenEndpoint)
        {
            Authenticator = new RestSharp.Authenticators.HttpBasicAuthenticator(
            reg.GetValue<string>("AppSettings:client_id")!,
            reg.GetValue<string>("AppSettings:client_secret")!)
        });
        var authReq = new RestRequest("", Method.Post);

        authReq.AddParameter("redirect_uri", reg.GetValue<string>("AppSettings:redirect")!);
        authReq.AddParameter("grant_type", "refresh_token");
        authReq.AddParameter("refresh_token", CredentialStore.GetRefreshToken(qbaName));
        var rsp = authClient.ExecuteRetry<QBAuth.Tokens>(authReq, retries: 1);

        if (rsp.IsSuccessful)
        {
            //Get previous expired credential using peek to pull environment from last run...
            var expCred = CredentialStore.PeekCredential(qbaName);

            return new RestSharpCredentialStoreItem(new RestSharp.Authenticators.JwtAuthenticator(rsp.Data?.AccessToken ?? ""), DateTimeOffset.UtcNow.AddSeconds(rsp.Data?.ExpiresIn ?? 3000))
            {
                RefreshToken = rsp.Data?.RefreshToken,
                StoreA = rsp.Data?.IdToken,
                Environment = expCred?.Environment ?? "",
            };
        }
        else
        {
            return new FaultedCredentialStoreItem($"Couldn't refresh token for realm {qbaName}, {rsp.Content}");
        }
    });
}

Re-Register the Callback on Application Startup 🔄

Since each realm has its own set of authorization details, it's important to re-register the callback functions for each realm when the application restarts:

foreach (var cred in CredentialStore.GetCredentialsByPredicate(f => f.Name.StartsWith("QBA-")))
{
    var QBAName = cred.Name;
    
    //TODO: Call the register function again listed above. It would be wise to encapsulate that mathod to use in multiple places!
}

Making Authorized Realm Calls Is Simpler Than Ever

Thanks to our prior setup with Perigee, making authorized realm calls is now a walk in the park. The system ensures that you get a freshly refreshed authorization code each time you hit an endpoint. Plus, it authorizes the call pre-emptively, reducing the risk of a failure midway through a process.

var realmID = 123456789;

//New client (this is using sandbox, you *should* be querying the discovery endpoint for this)
using var client = new RestClient(new RestClientOptions("https://sandbox-quickbooks.api.intuit.com") {
    
    //Use the credential authenticator here, with the QBA-Realm key! Voila!!!
    Authenticator = new CredentialAuthenticator($"QBA-{realmID}")
});

var rsp = client.ExecuteRetry<CompanyInfo.QueryResponse>($"/v3/company/{realmID}/query?query=select * from CompanyInfo", retries: 3);

In conclusion, while OAuth can often feel like a labyrinth of tokens and authorizations, Perigee simplifies the process, making it far easier and more reliable.

Happy coding from the Perigee Team!

PreviousAzure Service BusNextPerigee With .NET Hosting

Last updated 4 months ago

📣
Page cover image