fix(connectivity): Handle schemeless issuer URLs in JWT claims - #6948
fix(connectivity): Handle schemeless issuer URLs in JWT claims#6948IncognitoQuack wants to merge 2 commits into
Conversation
davidkna-sap
left a comment
There was a problem hiding this comment.
For parsing the URL, please switch to a simpler approach that avoids regular expressions e.g:
URL.canParse(issuer)
? new URL(issuer)
: new URL(`https://${issuer}`);5973bac to
20ff422
Compare
|
Thanks — switched to One thing worth flagging before you re-review: the literal form has an edge case I hit while URL.canParse('tenant.accounts.ondemand.com:8443'); // trueIt returns The version I pushed keeps your approach and adds a host check: const parsedIssuer = URL.canParse(issuer) ? new URL(issuer) : undefined;
const normalizedIssuer = parsedIssuer?.host ? issuer : `https://${issuer}`;I added two regression tests for it ( Happy to drop back to your exact snippet if you'd rather not carry the extra check — it's your call One disclosed behaviour change either way: |
Closes #6923.
An identity provider can be configured not to force
httpson the issuer URL, in which case theiss(orias_iss) claim arrives as a bare hostname such astenant.accounts.ondemand.com. TheURLconstructor rejects that, and two places in the connectivity package assume it will not.1.
getIssuerSubdomainthrows where its caller expects a falsy returngetIdentityServiceInstanceFromCredentials→extractSubdomainFromJwtcallsgetIssuerSubdomain(payload, true)and then does:The caller is written for a falsy return, but
getIssuerSubdomainthrows whenisValidUrl(issuer)is false, so the intended fallback never runs. The throw propagates to
retrieveServiceToken, whichcatches it and only logs a warning, so
serviceJwtstaysundefined,isSubscriberNeeded()returns
false, andgetDestination({ destinationName, jwt })silently reads the destination fromthe provider account instead of the subscriber account — the symptom reported in #6923.
2.
isIasTokenmisclassifies the same tokensisIasTokenparsesdecodedJwt.isswithnew URL(...)inside atrywhosecatchreturnsfalse, so a schemeless IAS issuer is classified as not an IAS token.getSubdomainthen takesthe XSUAA branch and calls
getIssuerSubdomain— which, before this change, throws, and with onlythe first fix in place returns a subdomain that is then sent as the
X-tenantheader ingetExchangeTenant. Fixing only the first function would turn a loud failure into a silently wrongtenant header, so both are fixed here.
Change
Schemeless issuers are prefixed with
https://before being parsed. Values that are not URLs at all(
'not a url','') are still rejected, and existinghttp://andhttps://issuers, issuerswith paths, and issuers with explicit ports are unaffected.
Note that
URL.canParsealone is not sufficient: it returnstruefortenant.accounts.ondemand.com:8443, which parses as a scheme with no host, so the prefix is appliedwhenever parsing does not yield a host. There are regression tests for that case.
Behaviour changes
iss: 'localhost'now throwsFailed to determine hostname: invalid host in "https://localhost/"instead of
Issuer URL in JWT is not a valid URL: "localhost".iss: 'https://'now throwsFailed to determine hostnameinstead ofis not a valid URL.Both cases still throw; only the messages differ.
Tests
subdomain-replacer.tshad no test coverage before this change. This PR addssubdomain-replacer.spec.tscovering bothgetIssuerSubdomainandreplaceSubdomain, includingthe pre-existing behaviour, and extends
jwt.spec.tswithdescribe('isIasToken()')anddescribe('getSubdomain()')blocks.