Add support for new adaptor Fayda ID#1721
Conversation
|
Thanks @IZO-Ong! @josephjclark @AishaHassen @PiusKariuki this is the first adaptor contribution from the YouthTech Singapore interns helping us out this summer 🙌 Would be great if y'all could find time to take a look. Some questions that came to mind from me while reviewing this:
|
|
The underlying issue for this isn't very well specced. @redietr @PiusKariuki can someone help me understand how Fayda would be used in a workflow, and what sort of funcionality the adaptor needs? Like is Fayda bascically just getting an access token/JWT which is then by other jobs? Or are we using it as a gateway to acutally pull information? |
|
@josephjclark from my understanding is that the adaptor is intended to use Fayda as an identity service, not just as a token provider. The access token is just part of the OAuth/OIDC flow. The actual purpose is to use that token to verify a person's identity and/or retrieve the identity data. So I think the adaptor should expose operations like:
|
|
Ok, so you use the adaptor to get an access token for other things, or to get information about a user. How would we expect that token to be used downstream? I guess we have to write it to state to be used by other jobs. Not ideal because that's presumably a sensitive token and we wouldn't have any protection for it. It'll appear in dataclips and logs and won't even be hidden by the UI scrubber. And you wouldn't try to extract it into a credential somehow. Do you have any more details about usage of these tokens? Would they have an expiry? Are there other auth systems that can be used? Maybe all the security stuff lately has got to my head, but it doesn't feel like we have a particualrly secure way of handling this right now. I can think of features that would help, but that's for down the road 🤔 |
|
I think the flow would be something like this: The user clicks Login with Fayda in the source app, enters their Fayda ID, and authenticates using OTP or biometrics. Once that works, Fayda redirects them back with an authorization code. Then I guess the source app passes that code to OpenFn, and the adaptor handles the rest in one go. It exchanges the code for the access token, uses the token to call UserInfo, validates the response, and returns the identity information the workflow needs. So the adaptor would have a function like So the token shouldn’t really need to be passed between jobs or written to state at all. It would just exist briefly inside that adaptor operation, and only the verified user details would move forward in the workflow. |
|
As for the expiry, the code and token both have very short expiry times (if my memory serves me correctly). |
|
@brandonjackson question for you. Why are we building a FAYDA adaptor instead of a MOSIP one? Fayda only serves Ethiopia whereas MOSIP would be applicable to anyone who wanted to use their id verification platform (e-signet). |
| // Sign a private_key_jwt client assertion for the token endpoint, as in | ||
| // the VeriFayda reference app. configuration.privateKey is a | ||
| // base64-encoded JWK. | ||
| export const generateClientAssertion = async configuration => { |
There was a problem hiding this comment.
Is this based on the Feyda docs example? Looks very similar, with the exeption that the private key appears to be JSON for some reason in their docs
There was a problem hiding this comment.
Yup. I also based it on the oidc test app that Fayda provided.
|
@IZO-Ong Thank you for the responsive commit but please hold off further development until we've agreed exactly what we're building here :) This thread is largely speculative (but your work is already helping us to understand exactly what we need) @redietr some more questions while I try to work this out:
This must be through a webhook then? So the app calls the openfn webhook, passing the code through there.
That sounds pretty good. We can keep the jwt off state, and So this is quite an unusual adaptor so it doesn't use a credential like most. It expects an authorisation code to be on input state, setup by whoever called it. I keep skimming over the Fayda API docs but they're over my head really. This is stuff that only make sense if you already understand it! |
|
|
Ah yes OK - I can see there is stuff we need to keep in the credential. Cool, so we can use keep that, and maybe add @IZO-Ong I think we've got enough clarity now that you can refactor a bit. See this comment:
So the only user facing operation will be Finally @IZO-Ong have you been able to actually run anything against a live server here? Has anyone given you a credential and maybe some test data to fetch? |
|
Noted on that @josephjclark, I will refactor along those lines. As for testing, unfortunately I don't have any credentials or test data yet to run against a live server. Let me know if someone can share those! |
|
@redietr I agree, I think we should explore whether Fayda could be served with a generic MOSIP or eSignet adaptor. It seems like the token generation step is useful but ultimately the getUserInfo function requires MOSIP. @IZO-Ong do you have thoughts on possible adaptor architectures here? eg
For reference attaching a doc from the MOSIP conference I went to which shows use cases very clearly |
|
Hi all, I've refactored the adaptor as per @josephjclark's notes. The adaptor now exposes a single operation getUserInfo(authorizationCode, options), and the client-assertion lifetime is configurable via |
|
@brandonjackson currently the Fayda adaptor can be treated like a generic eSignet / OIDC adaptor. eSignet is MOSIP's OIDC module, and getUserInfo just runs the standard authorise -> token -> userinfo flow to authenticate a person and return their verified KYC claims, so it does not directly call any MOSIP API; the same code will work for any MOSIP-based programme that uses eSignet. However, from my understanding, MOSIP does have other functionalities (ID-Auth matching, credential issuance, record updates) that are separate and would be its own generic MOSIP adaptor, so I do lean towards the design paradigm of option 2 (generic eSignet adaptor + generic MOSIP adaptor), and this adaptor can be renamed as an eSignet adaptor. |
Summary
Add a new adaptor,
@openfn/language-fayda, for Ethiopia's Fayda national digital ID system, handling authentication via eSignet's OIDC flow (JWK-signed client assertion + PKCE) and returning verified identity claims.Addresses Issue #1141
Details
What this adds
Three operations covering the full authorize -> token -> userinfo sequence:
getAuthorizationUrl(options)— builds the eSignet authorization URL (no network call; redirecting the user and collecting consent happens outside OpenFn). Also generates and returns a PKCEcodeVerifieronstate.data.getToken(code, options)— exchanges authorization code for tokens. Authenticates as the client with aprivate_key_jwtclient assertion (signed JWT) rather than a shared secret, per eSignet's requirements. Savesaccess_tokentostate.configurationso it's available to the next step without being passed explicitly.getUserInfo(accessToken, options)— fetches and decodes verified identity claims from the userinfo endpoint. Handles both a signed-JWT response and a plain-JSON response, since eSignet can return either depending on client configuration.Key decisions
fayda-auth-sdk, and found that it only implements a plain PKCE public-client flow and has no support for JWK/private_key_jwtclient assertions, which is the auth method eSignet's sandbox actually requires.National-ID-Program-Ethiopia/oidc-test-app). Uses the same library (jose, pinned to the same major version,^5.3.0), the same claim shape (iss/sub/aud, nojti), and the same 2-hour expiry. The official reference app sends both a signed client assertion and a PKCEcode_challenge/code_verifier. Added PKCE support to match, auto-generating a fresh verifier per call.Credentials
Sandbox
clientIdand signing key are pending application via the developer portal (~2 business days turnaround per the issue). Endpoint URLs in the configuration schema examples are illustrative placeholders.AI Usage
You can read more details in our
Responsible AI Policy
Review Checklist
Before merging, the reviewer should check the following items:
production? Is it safe to release? — N/A, new adaptor, nothing in production depends on it.
pnpm run versionused to bump versions (notpnpm changeset versiondirectly)? — N/A, no changeset.