Custom module
Next to the modules provided and integrated by the Comuny, custom modules can be specified as well. This allows adding new service providers as well as your very own providers. Custom modules are handled the same way as pre-packaged ones, so you can specify claims to be resolved during authentication as well as resolving them explicitly. The data provided by your own claims is stored in the same data store as for all other claims.
Every module specifies 3 types of claims. Module claim defines "name" of the module. Be requesting this claim, all standard and specific claims which belongs to the module will be resolved. Specific claims defines all the claims, module can resolve. Each one has its specific name and value. Some of the specific claims can also fulfill Standard claim which is defined by OIDC specification.
All the standard claims are defines in sealed class
de.comuny.wallet.core.dto.name.StandardClaimName
with proper names and types.
All specific claims extends from interface de.comuny.wallet.core.dto.name.SpecificClaimName
and also from module specific sealed class.
Example of custom module implementation
In this example, we will create custom module called SampleModule
To create custom module, there are several classes that needs to be implemented.
Module claim
First, we need definition of full module custom claim. Let's say, we want to have 2 values, timestamp and nickname
This class has to implement ModuleClaimValue
interface and according to ours naming
convention, we recommend to named it SampleModuleClaimValue
.
Specific claims
Second step, you have to define specific claim, per each claim value in the module claim.
As you can see, naming convention is similar to module claim, first module identification and
SpecificClaimName afterwards. This time, it's called SampleSpecificClaimName
and this class,
has to be generic (so we can define type per each claim) and has to implement
SpecificClaimName<T>
interface. Another recommendation is, that this class should "look like" ENUM, so we implemented
property values
which is located in companion object.
In this example, we want to resolve current time stamp as first specific claim, which will be stored as Long, and Nickname of the user. Nickname claim is also resolvable as Standard claim (see OpenID documentation), and implementation of that is directly in module class, which you can see in Module definition section.
Module events system
Usually, module wants to inform the app, what's going on, or often it has to receive some kind of values, either from the app or even from the user. For these purpose, we have to implement module event structure. (How to use this events from module's perspective can be seen in Module definition section)
To create event structure, we have to create sealed class
that implements
ResolvingEvent.OnResolveModuleEvent
interface, and we specify each event as
a child of that sealed class.
There are 2 types of events, informative events and requests.
Informative event can be implemented
as object or data class, and it's purpose is to inform the app, that something is happening.
We recommend defining Loading
event, that will be fired every time module is doing or
waiting for something.
To request any data from the user or the app, you have to create class with a function as a parameter, which will be triggered by the app. You can request any data you want, from android's context or activity, to any user specific data. When app received one of the request event, it can either directly respond to it or show the UI to the user.
As you can see, we are defining sealed class SampleModuleResolveEvent
which extends from
ResolvingEvent.OnResolveModuleEvent
. All the events extend from defined class.
OnSampleModuleLoading
is an object, that serves as informative event, and it will be emitted
every time module we want from app to show some kind of loading state.
OnNicknameRequest
is a request event, which serves as a form of requesting the nickname from
the user (or from app). Feel free to use any public properties or methods you case needs, but
for getting the response from this request, you have to add at least one more private lambda property
(doesn't have to be private, but implementation details should be hidden and exposed only by methods),
which gets triggered by the app. We also recommend adding at least 2 methods, one for positive answer
and one for negative.
If you create an object or class as an event we also recommend overriding toString
method.
Module definition
Now it's time to tie it all together by definition of the module. This class is also responsible for resolving standard and specific claims.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
|
Newly created module, has to be Kotlin object and has to extend TrinityModule
and provide
proper generic types, (module claim value and event structure). In our example, module is named
SampleModule
, it's module claim value is SampleModuleClaimValue
and event structure is
called SampleModuleResolveEvent
.
This class also has to implement few properties and methods. moduleConfigLinkName
identifies your module in OIDC configuration. Property moduleClaim
represents the name
of module claim. Set standardClaims
contains all standard claims, that module can resolve.
Set specificClaims
contains all specific claims.
Method resolveModule
contains all module's logic to resolve a claims. It can contain
communication with backend, informing the app what is processing or requesting a data
from the user. By calling emitter
we can emit events to the app.
Result of the resolving process, has to be properly signed JWS and it has to contain at least
one of the x5c or x5u header value. If your backend doesn't support signing yet, and you are
using development version of SDK, you can sign it by self generated keys using JwtHelper
class.
Functions getNormalized(Module)Value
serve as
converters between data stored by SDK and what claims are requested. Keep in mind,
getNormalizedValue
methods has to be able to resolve all defined standard or
specific claims.
If you want to use processIntegritytoken
with your modules, extend
de.comuny.wallet.core.module.TrinityModuleWithProcessIntegrityToken
instead of TrinityModule and definition
of ModuleClaimValue has to implement HasProcessIntegrityToken
interface.
Now your module functionresolveModule
contains processIntegrityToken value. All other aspects
of TrinityModule remains the same.