Hi Rick,
We are doing some oAuth stuff with gmail using webconnect as the server side and wwHTTP to make the request. We are getting back a JWT token as part of the response but we have no idea how to parse it / interpret it (i.e. it has the users email address in it). Is there a way in VFP / web connect to parse this or do you know of any built in C# stuff we could call with wwDotNetBridge without writing our own parser?
Thanks,
Scott
Never mind. Found another web call we can make that returns it back in normal JSON format. If you know a quick way to parse the JWT token great, if not, don't worry about it.
Thanks.
There are JWT Token classes in .NET. System.IdentityModel.JwtTokenHandler.
var token = "jwt-token-here";
var new JwtSecurityTokenHandler().ReadToken(token);
Note this will unpack the public data, but won't validate the token - there's a separate method for that to which you have to provide the keys to decrypt the token.
+++ Rick ---
Hey Rick,
Thanks! Is there a way to call this directly from wwDotNetBridge or do I need to write a c# dll and load it in to the dotNetBridge?
Thanks,
Scott
You should be able to call directly into this from FoxPro. Looks like it's a simple method call, but I'm not sure of the details required to parse the token. If I remember right this will decode the token but won't validate.
Token data is never secure - the secure part of the token is the signature that's used to verify that the data hasn't been tempered with.
+++ Rick ---
Hey Rick,
So I was able to load the assembly but I can't get method to work / can't create an instance to call the methods I need.
This is working to load the assembly
oBridge.loadAssembly('System.IdentityModel.Tokens.Jwt, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
IF !EMPTY(oBridge.cErrorMsg)
SET PATH TO (m.oldPath)
SELECT (m.oldArea)
RETURN 'Failed to Load JWT Assembly: ' + oBridge.cErrorMsg
ENDIF
However, I can't get an instance of the JwtSecurityTokenHandler or the JwtSecurityToken.
I've tried:
oBridge.CreateInstance('System.IdentityModel.Tokens.Jwt.JwtSecurityToken')
****
oBridge.CreateInstance('System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler')
****
oBridge.InvokeStaticMethod('Microsoft.IdentityModel.Tokens.SecurityTokenHandler', 'Create')
It always errors out the same with: Type is not loaded. Please make sure you call LoadAssembly first.
But the load assembly IS working (at least no error messages in oBridge.cErrorMsg). Any thoughts?
Thanks,
Scott
From what I see that library comes from a NuGet package so you have to get a hold of the package and the files in it.
Easiest way to do this I think:
Create a .NET Project file as a text file
// JwtTest.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.20.0" />
</ItemGroup>
</Project>
Open a command window at the location of the file. Make sure you have the .NET 6.0 SDK installed. Then build:
dotnet build
This creates a bin
folder and the package content will be dumped in it. you probably should move the files from there into your project folder (don't copy the output assembly just the System.
ones).
I spent way too much time on this, and this is probably one instance where I would have chosen to create a .NET wrapper 😄. BUt it's possible to do this with wwDotnetBridge:
CLEAR
DO wconnect
do wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = GetwwDotnetBridge()
? loBridge.LoadAssembly("C:\temp\TestLibrary\TestLibrary\bin\Debug\net472\System.IdentityModel.Tokens.Jwt.dll")
? loBridge.cErrorMsg
TEXT TO lcToken NOSHOW FLAGS 1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ENDTEXT
? lcToken
loInstance = loBridge.CreateInstance("System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler")
? loBridge.ceRRORMSG
? loInstance && object
loResult = loBridge.Invokemethod(loInstance, "ReadToken",lcToken)
? loResult && object
lopayload = loBridge.GetProperty(loResult,"PayLoad")
? loPayLoad
*** Old way passing by reference with ComValue
loResult = loBridge.CreateComValue()
loResult.Value = ""
? loBridge.InvokeMethod(loPayLoad,"TryGetValue","name",@loResult)
? loResult.Value
? loBridge.InvokeMethod(loPayLoad,"TryGetValue","sub",@loResult)
? loResult.Value
*** Simpler with updated version
? loBridge.odotnetBridge.GetDictionaryItem(loPayLoad, "name")
? loBridge.odotnetBridge.GetDictionaryItem(loPayLoad, "sub")
As I was working through this I realized that it's crazy difficult to get a result for a dictionary value so I added a new method to wwDotnetBridge to make this easier with GetDictionaryItem().
You can grab this updated version from:
https://west-wind.com/files/WebConnectionExpermental.zip
Easier with .NET
Looking at this I still think this is one of those cases where it makes sense to create a wrapper and just do it in .NET. You can play around with this in LinqPad to get it to work.
Here's the same code in .NET and it's a heck of a lot easier to understand what's going on:
void Main()
{
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
var handler = new JwtSecurityTokenHandler();
var parsed = handler.ReadJwtToken(token);
var sub = parsed.Payload["sub"];
sub.Dump();
var name = parsed.Payload["name"];
name.Dump();
parsed.Dump();
}
Especially since you kind of need to set up a .NET project anyway to get the NuGet package...
+++ Rick ---