This demo shows how you would authorize a token call to Microsoft Graph for delegated permissions where a token response is required.
This setup involves an app registration in Azure Portal, as well as registered redirects and the appropriately selected API Permissions.
To run the below demo, grab the Web Host Utilities and drop it into your project.
Our redirect is https://localhost:7201/api/token
- which is why our call to map the incoming HTTP request is /api/token
.
PerigeeApplication.ApplicationNoInit("GraphDemo", (c) =>
{
//Graph and token endpoint
var dvGraph = new GraphClient(
c.GetValue<string>("graph:tenant"),
c.GetValue<string>("graph:client"),
c.GetValue<string>("graph:secret"),
c.GetValue<string>("graph:scope"),
c.GetValue<string>("graph:redirect"),
"",
c.GetLogger<Program>());
//Add token receive endpoint
c.AddMinimalAPI("TokenReceiver", 7201, (w) => {
w.MapGet("/api/token", ([FromQuery] string code, GraphClient graph) => { CredentialStore.RefreshAuthorizationCode(graph.credentialName, code); return Results.Ok("Got the new code! Thanks!"); }); },
(b,s) => s.AddSingleton<GraphClient>(dvGraph));
//Await a valid credential on load
var cred = CredentialStore.AwaitValidCredential(dvGraph.credentialName, c.GetCancellationToken()).GetAwaiter().GetResult();
c.GetLogger<_PerigeeStartup>().LogInformation("Authorized Account: {acc}", cred.JWTGetValue(cred.DecodeJWT(cred.Authorization), "unique_name"));
});
The appsettings.json
:
"graph": {
"tenant": "tenantguid",
"client": "clientguid",
"secret": "secretkey",
"scope": "user.read Channel.ReadBasic.All Team.ReadBasic.All offline_access",
"redirect": "https://localhost:7201/api/token"
}
It's very important to include offline_access
if you want to be able to refresh the token automatically.
For DataVerse
If you're trying to communicate with DataVerse, simply change the domain and scope parameters:
PerigeeApplication.ApplicationNoInit("GraphDemo", (c) =>
{
//Graph and token endpoint
var dvGraph = new GraphAPI(
c.GetValue<string>("graph:tenant"),
c.GetValue<string>("graph:client"),
c.GetValue<string>("graph:secret"),
c.GetValue<string>("graph:scope"),
c.GetValue<string>("graph:redirect"),
c.GetValue<string>("graph:domain"),
c.GetLogger<Program>());
//Add token receive endpoint
c.AddMinimalAPI("TokenReceiver", 7201, (w) => {
w.MapGet("/api/token", ([FromQuery] string code, GraphClient graph) => { CredentialStore.RefreshAuthorizationCode(graph.credentialName, code); return Results.Ok("Got the new code! Thanks!"); }); },
(b,s) => s.AddSingleton<GraphClient>(dvGraph));
//Await a valid credential on load
var cred = CredentialStore.AwaitValidCredential(dvGraph.credentialName, c.GetCancellationToken()).GetAwaiter().GetResult();
c.GetLogger<_PerigeeStartup>().LogInformation("Authorized Account: {acc}", cred.JWTGetValue(cred.DecodeJWT(cred.Authorization), "unique_name"));
//Pull DV
var Employees = dvGraph.GetDataVerseTable<Employees>("cr521_employees");
});
The appsettings.json
:
"graph": {
"tenant": "tenantguid",
"client": "clientguid",
"secret": "secretkey",
"domain": "https://org1234567.api.crm.dynamics.com"
"scope": "https://org1234567.api.crm.dynamics.com/.default offline_access",
"redirect": "https://localhost:7201/api/token"
}