OAuth with Blazor App

980
3
07-28-2022 10:28 AM
JoeHershman
MVP Regular Contributor

Hi,

I am wondering if anyone has tried (successfully) to setup OAuth in a Blazor Web Assembly.  I think I have things configured based on the documentation but continue to get an error saying it is not setup correct.  Without any specifics.

Thanks - joe

 

Thanks,
-Joe
0 Kudos
3 Replies
Raul_Jimenez
Esri Contributor

I haven't @joe but if you solve it let us know how!!

Thanks and good luck!

0 Kudos
JoeHershman
MVP Regular Contributor

In the end is pretty straight forward.  The simplest way is to simply get a token directly from a call to the /authorize method.  This was done by navigating to the authorize Url in the OnInitialized method of the Index.razor page

 

protected override void OnInitialized()
{
	try
	{
		string authorizeUrl = "https://www.arcgis.com/sharing/rest/oauth2/authorize";
		string clientId = Configuration["Authorization:clientId"]!.ToString();
		string redirectUrl = Configuration["Authorization:redirectUrl"]!.ToString();
		string responseType = "token";

		UriBuilder builder = new UriBuilder(authorizeUrl)
		{
			Query = $"client_id={clientId}&redirect_uri={redirectUrl}&response_type={responseType}"
		};

		string oAuthUrl = builder.ToString();
		NavigationManager.NavigateTo(oAuthUrl);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
}

 

Then in the page defined in the redirectUrl you grab the token from the return returned url

 

protected override void OnInitialized()
{
	try
	{
		_token =  Navigation.Uri.Substring(Navigation.Uri.IndexOf("=", StringComparison.Ordinal) + 1);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
}

 

 An approach which I think is a bit more secure would involve requesting code from /authorize method and then getting the token in the redirect page using the /token method.  In this case in the Index.razor we have

 

protected override void OnInitialized()
{
	try
	{
		string authorizeUrl = "https://www.arcgis.com/sharing/rest/oauth2/authorize";
		string clientId = Configuration["Authorization:clientId"]!.ToString();
		string redirectUrl = Configuration["Authorization:redirectUrl"]!.ToString();
		string responseType = "code";
		string codeChallenge = CreateChallangeCode();
		string codeChallengeMethod = "plain";

		UriBuilder builder = new UriBuilder(authorizeUrl)
		{
			Query = $"client_id={clientId}&redirect_uri={redirectUrl}&response_type={responseType}&code_challenge={codeChallenge}&code_challenge_method={codeChallengeMethod}"
		};

		string oAuthUrl = builder.ToString();
		NavigationManager.NavigateTo(oAuthUrl);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
}

private string CreateChallangeCode()
{
	using SHA256 sha256 = SHA256.Create();
	byte[] data = sha256.ComputeHash(Encoding.UTF8.GetBytes("blazor"));

	var builder = new StringBuilder();

	// Loop through each byte of the hashed data
	// and format each one as a hexadecimal string.
	foreach (var b in data)
	{
		builder.Append(b.ToString("x2"));
	}


	return builder.ToString();
}

 

and in the redirect page get the token from the returned code

 

protected override async void OnInitialized()
{
	try
	{
		string code =  Navigation.Uri.Substring(Navigation.Uri.IndexOf("=", StringComparison.Ordinal) + 1);
		await RequestToken(code);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
}

private async Task RequestToken(string code)
{
	string tokenUrl = "https://www.arcgis.com/sharing/rest/oauth2/token";
	string clientId = Configuration["Authorization:clientId"]!;
	string redirectUrl = Configuration["Authorization:redirectUrl"]!;
	string codeChallenge = CreateCodeChallenge();

	var dictionary = new Dictionary<string, string>
	{
		{ "client_id", clientId },
		{ "grant_type", "authorization_code" },
		{ "code", code! },
		{ "code_verifier", codeChallenge },
		{ "redirect_uri", redirectUrl }
	};

	FormUrlEncodedContent content = new FormUrlEncodedContent(dictionary);


	using HttpClient client = new HttpClient();
	var response = await client.PostAsync(tokenUrl, content);
	var json = await response.Content.ReadAsStringAsync();

	JsonNode node = JsonNode.Parse(json)!;

	_token = node!["access_token"]!.ToString();
}

 

Thanks,
-Joe
Raul_Jimenez
Esri Contributor

Awesome @joe !! thanks for taking the time to share the solution 🙂

0 Kudos