Linux ext-jwt-signer w/Google OIDC

I'm new with Ziti, so I'm trying to figure out all the configuration options.

My first success was to run the linux-edge-tunnel with a host token. All good. DNS sees my ziti hosts and I can reach my service.

My next step was to try and setup OIDC with Google. Here's what I did:

  1. I created a new client in Google Auth Platform using Universal Windows Platform
  2. Created a new identity in ZAC with my google email as the externalID.
  3. I set up an jwt-signer in ZAC following the wiki (I think) using the clientID in audience and clientID, email claim, jwks, https://acounts.google.com as the issuer and auth url. with email as the scope.

On my client, I used ziti ops verify ext-jwt-signer oidc --authenticate GoogleVV --ca server.pem --access-token --id-token

I see the ID Token arrive and decode. It looks like what I expect. I see the email field and it matches my identity.

Then I get the warning message:
WARNING access token not parsed: token doesn't appear to be a jwt/is opaque. cannot display payload
That doesn't look good.

If I look at the console log I see:
jwt failed to parse: token is malformed: token contains an invalid number of segments

The raw access token is printed and doesn't look like a JWT. It starts with 4 characters then a period, then a long chunk of text that might be separated with -. Obviously don't want to include that here.

I struggled with this for the longest time. I eventually changed the targetTokenType in the signer to ID rather than access. That got me to login succeeded

That option doesn't seem to be described well in the docs. What's the difference between the two? Is that the right thing to do?

Hopefully this helps someone. Thanks in advance!

Hi @Daryll, welcome to the community and to OpenZiti!

Debugging a PKCE flow can be quite challenging, so nice going figuring out that the token type can be changed to ID from Access! Yes, that most definitely should have been in the docs. I wrote those docs and I'm surprised I left that out! I thought I'd added it to the docs but definitely don't see it mentioned and would indeed be a good thing to add to the docs.

There are a lot of resources you can find on the internet describing the difference between an access token and an ID token and what system should get either and how to use them. It's too big of a topic to describe here but you can find a bunch of blogs about that to read (all the same ones I've read no doubt).

Yes it's the right thing to do. OpenZiti's ext-jwt-signers work by taking a token from an IdP, validating the token, and then using claims from the token to map to OpenZiti identities. The token presented to OpenZiti must be a JWT. Generally, it's "most correct" to use the Access token. That's why it's the default that's selected. However some IdPs don't allow these access tokens to be parsed, they are opaque as you also noted. If the IdP doesn't provide a JWT access token and uses an opaque token, your only option is to use the ID token which is mandated by the spec to be a JWT.

So you got it sorted and you did the needful to use Google as an IdP.

Thanks again, here's what the doc will look like once the PR merges

Yes, I could make ziti ops verify ext-jwt-signer oidc go through the process.

Now I'm trying to get ziti-edge-tunnel on linux with the OIDC working. I can't seem to find useful documentation for this process. Can you point me at something?

From what I've figured out so far. Now that my jwt signer is setup, I downloaded the network.jwt from Zac.

I used ziti-edge-tunnel add -i network -j JWT to add the identity. That seemed to work (returned success) and network.json was added to /opt/openziti/etc/identities. That was the same process I used to add the host identity in my previous experiment. Restarted ziti-edge-tunnel just to be sure. Systemctl status says it is running.

I think what I want to do next is ziti-edge-tunnel ext-jwt-login, That expects an identity, but I'm not sure what to use there. I tried -i daryll thinking it might need a new filename. It also allows a provider which seems like it should be GoogleVV like I did with ops verify.

What I get back is:
{
"Success":false,
"Error":"ziti context not found",
"Code":500
}

I have ZITI_VERBOSE set to debug in the ziti-edge-tunnel.env, but that doesn't seem to generate anything useful in the ziti-edge-tunnel logs. All I see are messages about ziti:dns-resolver doing a bunch of stuff.

One other gotcha I want to make sure I mention. The controller is using a self signed certificate. I've had to pass the --ca switch to ziti ops verify to get that to work. I was assuming the cert was embedded in the network jwt. There's no --ca for the ext-jwt-login.

At this point I'm stumped. I must be doing something wrong. Any suggestions how to proceed?

Thanks

You would create an identity in the ziti CLI/ZAC and assign the proper external id to your identity. Have you done that? That might be the missing step? It's only kind of mentioned in the "Configuring OIDC" page so I can see how that might be easy to miss?

Yes, I did that. As I said before, the ziti ops verify oidc worked. It launched google in my browser, I selected my identity, and it returned a token and success. I'm assuming the flow is good. I'm just having trouble getting ziti-edge-tunnel to perform the same thing.

Oh I understand now. Sorry about that, it didn't click. You need to pass the absolute path to the identity file. Are you perhaps using a relative path?

I definitely wasn't passing an absolute path.

If I pass a path to the /opt/openziti/etc/identities/network.json (that I added previously) then I get back JSON with:
{
Success: true
Data:
identifier: PATH TO MY network.json
url: urlencoded URL to accounts.google.com auth endpoint with appropriate parameters
Code: 0
}

If I massage the URL a bit I can paste it into a browser. I fixed / for slashes for the URL. The result is the google part of the flow. I select my account. Then it redirects back to localhost:20314, but nothing is running there. Is the ziti-edge-tunnel supposed to be listening for that redirect?

Yes, most definitely. Did you perhaps take > 30s to complete the flow? When you run ziti-edge-tunnel ext-jwt-login it'll listen on port 20314 for 30 seconds. I'm guessing since it was your first time, the url decoding and copying/pasting took more than 30s and the listening socket was shut down.

Yep. The 30 sec timeout was biting me.

In all your JSON output you seem to quote the '/'. Is that a bug? It's really annoying! :grinning_face:

I ended up doing: ziti-edge-tunnel ext-jwt-login -i /opt/openziti/etc/identities/network.son | sed 's/\//g' ; sleep 30 ; beep

Now if I look at identities in Zac, I see:
zac-identities.jpg

The OS is now Linux which is good. The green dot by Daryll isn't lit. (after a screen refresh)
If I open the identity it says "Online" so that's good. I don't have any services assigned to Daryll yet. Is the lack of green dot a bug?

This looks like I got the identity registered and online, so my next step is connecting services. I think I know how to do that.

Making progress!

I'm up and running now with my OIDC identity now.

To answer my own question. The green dot by my identity wasn't lit, because my Identity didn't have a router policy that allowed me to connect to the edge router that was running my services. As soon as I added that the green dot came back on. That's router connection status, not ziti service connection status.

I created services for nfsd, portmapper, and mountd, and then voila I can mount nfs through my ziti host. Whooo hoo!

I still think the backslashes are a bug. If I do ziti-edge-tunnel router-status I see things like:
{
"Success":true,
"Data":{
"Active":false,
"Duration":8934116,
"StartTime":"2025-06-30T18:27:54.090754Z",
"Identities":[

{
"Name":"Daryll",
"Identifier":"/opt/openziti/etc/identities/network.json",

...
"LogLevel":"debug",
"ServiceVersion":{
"Version":"v1.6.1",
"BuildDate":"Tue-06/10/2025-17:19:56-UTC"
},

But I'm happy everything is working and I can workaround this for the time being.

Thanks again for all the help getting this going.

1 Like

Hrm. That seems unexpected for sure. Could you share the json you're seeing? You can send me a DM here in discourse if you like. I was able to see what you're saying. Yeah that's not what i'd expect either:

(3105087)[       18.846]   TRACE tunnel-cbs:ziti_tunnel_ctrl.c:226 process_cmd() processing command[ExternalAuth] with data[{ "Identifier": "\/mnt\/c\/temp\/ids\/ext.json" }]

We have some high priority issues we are working on but if you can send me what you had to deal with, it'll help me understand better. If not, hopefully me or someone will get some time to reproduce what you're seeing and file an issue.

Thanks for letting us know and nice job getting it going. Cheers

i filed a bug to track it here json strings with slashes are escaped · Issue #1182 · openziti/ziti-tunnel-sdk-c · GitHub