# Alternative access for Tenant management without needing Wallet ID and Key. ### Refresher on Traction Architechture ![](https://i.imgur.com/HPc3lB1.jpg) ## Background As a multi-tenanted ACA-Py solution with additional plugins for wallet settings, [Traction](https://github.com/bcgov/traction) lets users manage their Tenants (wallets) themselves. This can be accomplished with API calls to [Traction](https://github.com/bcgov/traction) directly, or through the Traction Tenant UI. Access to a Tenant is secured by the user obtaining a bearer token ("logging in") using their wallet ID and Key as the credentials. Calls to Traction are additionally secured by only allowing Tenant calls through a proxy that ensures they are only accessing endpoints for their multi-tenanted usage and do not require ever getting the x-api-key for ACA-Py (though this could be up to implementation and operational needs of a Traction system, optional if using the `x-api-key` is desired). ![](https://i.imgur.com/vflhtyR.png) `Fig. Tenant user logging in with Wallet Key through Tenant UI` ![](https://i.imgur.com/miFjHMZ.png) `Fig. Tenant user managing settings` ## Problem In order to access Traction through the Tenant UI, a user is currently providing the actual **KEY** for the wallet. This isn't what we would want operationally from a security perspective for many reasons. - Needing to look at this wallet key and copy paste it each time - If multiple business area users need to manage that Tenant, they need to know the key. Obviously not ideal as that key is not a user-based access and then someone who leaves a team or project would still have that key known to them. ## Possible Solution(s) Federating access to the Tenant using existing accounts would be ideal. Someone should just be granted a role as a manager of that Tenant to their identity or credentials. OIDC would be the easiest and most usable solution for our current state. If Traction supports OIDC login for a tenant a user could, for example: - Have their IDIR associated with a Tenant in a Tenant Management role and then log in with Keycloak - Be granted a credential associating the user with the Tenant and then a solution such as VC-Authn could act as OIDC login. ## Current Tenant UI State The Tenant UI currently does allow an OIDC login, only for the Innkeeper side. The Innkeeper themselves is a Tenant as well. This is simpler to control, as there is one fixed Innkeeper Tenant, and the backend of the Tenant UI can have secure access to that Tenant's Wallet Key without needing to expose it to anything external. The Tenant UI (in our current BC Gov implementation, this could vary depending on how the Traction operator wishes to host things, though the concept would be the same) is deployed alongside Traction and could be considered "part of" Traction and thus can access the configured secret for the Innkeeper key environmentally. This would of course not apply to dynamically created Tenants where only the client creating and using the Tenant has the key stored by themselves. The current Innkeeper OIDC login works in the following manner: - Tenant UI backend has optional configuration for Typical OIDC setup - Tenant UI backend has access to Innkeeper secret through environment - The front end logs in a user through the configured OIDC provider (and can optionally check a role) - The token returned to the FE user is sent to the Tenant UI backend on successful login callback, validated - If valid then a ACA-Py token is fetched by the backend and returned to the user, and login proceeds the same from there just as if a provided wallet ID/key were used instead. - In this current model the JWT from OIDC auth is only used at that one login stage just to get the ACA-Py bearer token. ![](https://i.imgur.com/07zu6gH.png) `Fig. Innkeeper login with IDIR OIDC option` ![](https://i.imgur.com/Q6LiU5V.png) `Fig. Tenant UI arch, showing Innkeeper OIDC` So this concept could be expanded on for the Tenant side, with some additional infrastructure and decisions along the way. ## Descision and thoughts on Key ownership Before proceeding too much, one fairly big concept would have to be understood by any stakeholders or clients (Tenants) and that is that: - In the current model the wallet key is provided to the line of business requesting the Tenant through the reservation process. The LOB user requesting it is the only person that ever sees that key (unless operationally configured to allow the Innkeeper to see it once). They then keep that key under their chosen security model. - In any solution to map an OIDC user to the wallet key, that key would likely be stored somewhere to broker the authorized OIDC user to get the Traction bearer token. So any decisions on moving to a more centralized key storage for this would have to be understood. Of course this could be optional and a LOB user could not use the OIDC access for Tenant management and Tenant UI usage. They can also do all the Tenant management without the Tenant UI and could broker that authentication in their own model keeping their key under their own security needs. ## OIDC "User Directory" Solution In the current Tenant Reservation (the onboarding process) model the Innkeeper approves an access request and the public user making the Reservation is then allowed to 1-time make the call that creates the Tenant/wallet. The key is then returned to their screen, not stored or cached, and the user can't get it again if they leave. ![](https://i.imgur.com/lpqPq9U.png) `Fig. Tenant getting their key` To allow this user to then access the Tenant UI with an identity provider instead, an identifier for that user (in a configured OIDC setup) would need to be mapped to the wallet authentication details. ![](https://i.imgur.com/sJNej9u.png) `Fig. OIDC to Wallet Auth mapping` The Tenant UI would then authenticate the user upon logging in to the OIDC provider and use the mapped (decrypted) wallet ID and key to get the bearer token needed to make the Traction/ACA-Py calls. ![](https://i.imgur.com/NVwvhjp.png) `Fig. Bearer Token (T) being accessed from association with authenticated user's JWT.` **NOTE:** This all need not be specific to the Tenant UI. The user directory could be used to broker this association from other frontend clients or other non-FE processes of course in other Traction implementations or setups. But for this discussion it will all be referred to through the Tenant UI. ### Model 1: Tenant UI gets bearer token once and uses that. This would be as described in the current Innkeeper setup above. On logging in the Tenant UI would call this User Directory service with the JWT, it would validate, figure out which user it is, access the wallet auth details, log in to get the bearer token and return that to the user's frontend session. The bearer token would be used for each call directly to Traction (or rather, the Traction NGINX layer that provides the x-api-key). ### Model 2: Tenant UI uses the JWT before each call to Traction. This would instead have the frontend of the Tenant UI call the Tenant UI backend for each action with the logged in JWT. The backend would then swap in the bearer token and proxy that over to Traction NGINX. In order to avoid swamping Traction with token fetch calls constantly it would probably be best then to implement some caching of the bearer token in the backend. ### Note on Proxies Possible layers involved in either model would be - Tenant UI backend - Hypothetical User Directory service - Traction NGINX proxy Refer to figure at top for Traction arch. So it could be worth examining this current and planned state to see if some of this should be reconfigured. For example, the Tenant UI Backend (a Node API) serves the FE Vue app as well as validates OIDC tokens and logs the user in (returning the bearer token). It also handles some email things and other minor business logic. Maybe the FE should be served on it's own from a lighter server (Caddy etc) and this Node API and User Directory should be the same thing... The NGINX proxy probably could remain on it's own as it has no association with the Tenant UI and LOB apps calling Traction from an API standpoint would still need to go through that as they can never be given the x-api-key, which is needed to call ACA-Py. At least under the current ACA-Py multi-tenancy system. ## Next Steps/Implementation Details and Questions There's a few directions to go with this implementation idea. See the Example Implementation section below for probably the simplest, but future direction should take some of these questions into account ### Question 1: Where should User Directory live The crucial piece of the scheme is mapping a user to a wallet auth details (or more than 1). - Traction plugin? - Part of Tenant UI? - Separate service? - Somehow integrated in OIDC provider? ### Question 2: What security considerations are needed for storing the wallet auth Some persistence is needed somewhere to associate a user to a wallet ID and key. What needs to be considered in that persistence in terms of storying the wallet details. How would the service fetch/decrypt the key to use to call Traction to get the Bearer token? ### Question 3: Where would the OIDC configuration live and how much configuration is available. - Does the Tenant UI handle the OIDC config, or would the "User Directory" service manage this (depending on answers to question 1) - Can a Traction ecosystem operator configure multiple OIDC options? ### Question 4: How does "Team Management" for an OIDC->Wallet mapping work. - We can map a OIDC user to a wallet, but how much beyond that is needed? - Should there be multiple roles and self-serve Team Management? IE can a "wallet owner" then delegate wallet access to other OIDC users? - Where would this interface or management system live. ### Question 5: How would the initial mapping work - When an Innkeeper authorizes a client to create their Tenant, assuming the client wants to map an identity to the wallet, who would do that mapping and where? Questions 4 and 5 could be abstracted away as manual setup or scripts or something for any proof-of-concept or initial implementations of this. ## Example Implementation This is a simple first way of proof-of-concepting this if we want to try it out and move forward. ### Steps 1. The user logs in with a configured OIDC provider. OIDC config is contained in the Tenant UI app. 2. The JWT is passed to the Tenant UI backend 3. The Tenant UI requests a wallet log in through the User Directory service using that JWT 4. The User Directory validates the JWT (it also has OIDC config, or refers to the same config) and looks up the user, getting the wallet auth 5. The User Directory gets a token from Traction using the wallet id/key. 6. The wallet token returns to the Tenant UI through to the user 7. Tenant UI operations proceed as normal through the current implementation ![](https://i.imgur.com/zK5doRo.png) `Fig. User Directory Flow Chart`