Skip to content

Commit b3c96dd

Browse files
committed
add csharp example for azure function to azure-function-token-provider
1 parent 4fd7fb4 commit b3c96dd

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

articles/azure-fluid-relay/how-tos/azure-function-token-provider.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Using [Azure Functions](../../azure-functions/functions-overview.md) is a fast w
3434

3535
This example demonstrates how to create your own **HTTPTrigger Azure Function** that fetches the token by passing in your tenant key.
3636

37+
# [TypeScript](#tab/typescript)
38+
3739
```typescript
3840
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
3941
import { ScopeType } from "@fluidframework/azure-client";
@@ -88,6 +90,103 @@ export default httpTrigger;
8890

8991
The `generateToken` function, found in the `@fluidframework/azure-service-utils` package, generates a token for the given user that is signed using the tenant's secret key. This method enables the token to be returned to the client without exposing the secret. Instead, the token is generated server-side using the secret to provide scoped access to the given document. The example ITokenProvider below makes HTTP requests to this Azure Function to retrieve the tokens.
9092

93+
# [C#](#tab/csharp)
94+
95+
```cs
96+
using System;
97+
using System.IO;
98+
using System.Threading.Tasks;
99+
using Microsoft.AspNetCore.Mvc;
100+
using Microsoft.Azure.WebJobs;
101+
using Microsoft.Azure.WebJobs.Extensions.Http;
102+
using Microsoft.AspNetCore.Http;
103+
using Microsoft.Extensions.Logging;
104+
using Newtonsoft.Json;
105+
using Newtonsoft.Json.Linq;
106+
using System.Text;
107+
108+
using Microsoft.IdentityModel.Tokens;
109+
using System.IdentityModel.Tokens.Jwt;
110+
111+
namespace dotnet_tokenprovider_functionsapp
112+
{
113+
public static class AzureFunction
114+
{
115+
// NOTE: retrieve the key from a secure location.
116+
private static readonly string key = "myTenantKey";
117+
118+
[FunctionName("AzureFunction")]
119+
public static async Task<IActionResult> Run(
120+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger log)
121+
{
122+
string content = await new StreamReader(req.Body).ReadToEndAsync();
123+
JObject body = !string.IsNullOrEmpty(content) ? JObject.Parse(content) : null;
124+
125+
string tenantId = (req.Query["tenantId"].ToString() ?? body["tenantId"]?.ToString()) as string;
126+
string documentId = (req.Query["documentId"].ToString() ?? body["documentId"]?.ToString() ?? null) as string;
127+
string userId = (req.Query["userId"].ToString() ?? body["userId"]?.ToString()) as string;
128+
string userName = (req.Query["userName"].ToString() ?? body["userName"]?.ToString()) as string;
129+
string[] scopes = (req.Query["scopes"].ToString().Split(",") ?? body["scopes"]?.ToString().Split(",") ?? null) as string[];
130+
131+
if (string.IsNullOrEmpty(tenantId))
132+
{
133+
return new BadRequestObjectResult("No tenantId provided in query params");
134+
}
135+
136+
if (string.IsNullOrEmpty(key))
137+
{
138+
return new NotFoundObjectResult($"No key found for the provided tenantId: ${tenantId}");
139+
}
140+
141+
// If a user is not specified, the token will not be associated with a user, and a randomly generated mock user will be used instead
142+
var user = (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(userId)) ?
143+
new { name = Guid.NewGuid().ToString(), id = Guid.NewGuid().ToString() } :
144+
new { name = userName, id = userId };
145+
146+
// Will generate the token and returned by an ITokenProvider implementation to use with the AzureClient.
147+
string token = GenerateToken(
148+
tenantId,
149+
key,
150+
scopes ?? new string[] { "doc:read", "doc:write", "summary:write" },
151+
documentId,
152+
user
153+
);
154+
155+
return new OkObjectResult(token);
156+
}
157+
158+
private static string GenerateToken(string tenantId, string key, string[] scopes, string? documentId, dynamic user, int lifetime = 3600, string ver = "1.0")
159+
{
160+
string docId = documentId ?? "";
161+
DateTime now = DateTime.Now;
162+
163+
SigningCredentials credentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), SecurityAlgorithms.HmacSha256);
164+
165+
JwtHeader header = new JwtHeader(credentials);
166+
JwtPayload payload = new JwtPayload
167+
{
168+
{ "documentId", docId },
169+
{ "scopes", scopes },
170+
{ "tenantId", tenantId },
171+
{ "user", user },
172+
{ "iat", new DateTimeOffset(now).ToUnixTimeSeconds() },
173+
{ "exp", new DateTimeOffset(now.AddSeconds(lifetime)).ToUnixTimeSeconds() },
174+
{ "ver", ver },
175+
{ "jti", Guid.NewGuid() }
176+
};
177+
178+
JwtSecurityToken token = new JwtSecurityToken(header, payload);
179+
180+
return new JwtSecurityTokenHandler().WriteToken(token);
181+
}
182+
}
183+
}
184+
```
185+
186+
The `GenerateToken` function is built off of the `@fluidframework/azure-service-utils` npm package, and it generates a token for the given user that is signed using the tenant's secret key. This function enables the token to be returned to the client without exposing the secret. Instead, the token is generated server-side using the secret to provide scoped access to the given document. The example ITokenProvider below makes HTTP requests to this Azure Function to retrieve the tokens.
187+
188+
---
189+
91190
### Deploy the Azure Function
92191

93192
Azure Functions can be deployed in several ways. For more information, see the **Deploy** section of the [Azure Functions documentation](../../azure-functions/functions-continuous-deployment.md) for more information about deploying Azure Functions.

0 commit comments

Comments
 (0)