-
Notifications
You must be signed in to change notification settings - Fork 32
Description
Narrative
I'm a naïve user. I just want a token:
$ pelican token create osdf://ospool/ap40/data --read
WARNING[2026-01-05T08:38:39-06:00] Unable to get JWKS from issuer URL https://osg-htc.org/ospool: Error getting JWKS URL from issuer URL: failed to unmarshal openid-configuration for issuer https://osg-htc.org/ospool: json: cannot unmarshal array into Go value of type string; skipping
Error: unable to determine issuer for resource osdf://ospool/ap40/data; you may need to re-run with '--issuer <issuer URL>' to specify an issuer: none of the issuers discovered at the director match your signing key; issuers that were checked: https://osg-htc.org/ospool
Usage:
pelican token create <pelican-url> [flags]
Examples:
To create a read/write token for /some/namespace/path in OSDF: pelican token create --read --write pelican://osg-htc.org/some/namespace/path
Flags:
-a, --audience string Specify the token's 'audience/aud' claim. If not provided, the equivalent 'any' audience for the selected profile will be used (e.g. 'https://wlcg.cern.ch/jwt/v1/any' for the 'wlcg' profile).
-h, --help help for create
-i, --issuer string Set the token's 'issuer/iss' claim. If not provided, the issuer will be discovered via the Director.
-l, --lifetime int Set the token's lifetime in seconds. (default 1200)
-m, --modify Indicate the requested token should provide the ability to modify/delete the specified resource.
-k, --private-key string Path to the private key used to sign the token. If not provided, Pelican will look for the private key in the default location pointed to by the 'IssuerKeysDirectory' config parameter.
-p, --profile string Create a token with a specific JWT profile. Accepted values are scitokens2 and wlcg. (default "wlcg")
--raw-claim stringArray Set claims to be added to the token. Format: <claim_key>=<claim_value>.
--raw-scope stringArray Set non-typical values for the token's 'scope' claim. Scopes should be space-separated, e.g. 'storage.read:/ storage.create:/'.
-r, --read Indicate the requested token should provide the ability to read the specified resource.
--scope-path string Specify the path to use when creating the token's scopes. This should generally be the object path without the namespace prefix.
-s, --stage Indicate the requested token should provide the ability to stage the specified resource.
--subject string Set token's 'subject/sub' claim. If not provided, the current user will be used as the default subject.
-w, --write Indicate the requested token should provide the ability to create/write the specified resource. Does not grant the ability to overwrite/modify existing resources.
Global Flags:
--config string config file (default is $HOME/.config/pelican/pelican.yaml)
-d, --debug Enable debug log messages
-f, --federation string Pelican federation to utilize
--json output results in JSON format
-L, --log string Specified log output file
--version Print the version and exit
ERROR[2026-01-05T08:38:39-06:00] Fatal error occurred at the start of the program. Cleanup started: unable to determine issuer for resource osdf://ospool/ap40/data; you may need to re-run with '--issuer <issuer URL>' to specify an issuer: none of the issuers discovered at the director match your signing key; issuers that were checked: https://osg-htc.org/ospool
This impressive amount of text completely obscures my actual mistake: "none of the issuers discovered at the director match your signing key".
-
"unable to determine issuer for resource osdf://ospool/ap40/data" is emitted twice, and it reads like a lie both times, by the client's own admission: "issuers that were checked: https://osg-htc.org/ospool".
-
The initial warning that "Unable to get JWKS from issuer URL https://osg-htc.org/ospool" is actually a straight up bug in the client.
Details
Pelican version:
Version: 7.22.0
Build Date: 2025-12-19T19:00:13Z
Build Commit: 074a9766f8c06e281c4fbfeb98a205f021288b56
Built By: goreleaser
Problem the First
The "Unable to get JWKS from issuer URL" warning comes from
Lines 157 to 163 in c8edb77
| // Comb through the JWKS from each issuer to find which matches the signing key | |
| for _, issuer := range directorInfo.XPelAuthHdr.Issuers { | |
| remoteJWKS, err := server_utils.GetJWKSFromIssUrl(issuer.String()) | |
| if err != nil { | |
| log.Warningf("Unable to get JWKS from issuer URL %s: %v; skipping", issuer, err) | |
| continue | |
| } |
Tracing backwards through the rest of the warning message and the code, we eventually arrive at
pelican/server_utils/registry.go
Lines 98 to 102 in c8edb77
| var openIDCfgMap map[string]string | |
| err = json.Unmarshal(body, &openIDCfgMap) | |
| if err != nil { | |
| return "", errors.Wrapf(err, "failed to unmarshal openid-configuration for issuer %s", issuerUrl) | |
| } |
The type of openIDCfgMap is simply incorrect. A quick check of https://osg-htc.org/ospool/.well-known/openid-configuration (or https://cilogon.org/.well-known/openid-configuration, or what have you) reveals that some of the string keys can indeed have array values:
{
"issuer":"https://osg-htc.org/ospool",
"jwks_uri":"https://osg-htc.org/ospool/oauth2/certs",
"token_endpoint": "https://osdf-ospool-issuer.osgdev.chtc.io/scitokens-server/token",
"userinfo_endpoint": "https://osdf-ospool-issuer.osgdev.chtc.io/scitokens-server/userinfo",
"registration_endpoint": "https://osdf-ospool-issuer.osgdev.chtc.io/scitokens-server/oidc-cm",
"device_authorization_endpoint": "https://osdf-ospool-issuer.osgdev.chtc.io/scitokens-server/device_authorization",
"authorization_endpoint": "https://osdf-ospool-issuer.osgdev.chtc.io/scitokens-server/authorize",
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic"
],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code"
]
}
Problem the Second
The "unable to determine issuer for resource" error comes from
Lines 298 to 304 in c8edb77
| if issuer == "" { | |
| // If no issuer is provided, try to discover it from the info we previously obtained | |
| // from the Director | |
| issuer, err = getIssuer(directorInfo, kidSet) | |
| if err != nil { | |
| return errors.Wrapf(err, "unable to determine issuer for resource %s; you may need to re-run with '--issuer <issuer URL>' to specify an issuer", rawUrl) | |
| } |
which would seem to take as through the flow in the previous problem, ending at
Lines 184 to 185 in c8edb77
| return "", errors.Errorf("none of the issuers discovered at the director match your signing key; issuers that were checked: %s", | |
| combineUrls(directorInfo.XPelAuthHdr.Issuers)) |