Custom Identity Providers #
Pragma Engine offers the flexibility to create multiple custom identity providers. To create a custom identity provider, there are several steps that need to be taken:
- Add a new enum value.
- Create an Identity Provider Plugin class.
- Configure the Identity Provider Plugin in Pragma Engine.
- [optional] Set up Portal login.
Add a new enum value #
- In the
AccountRpcExt.proto
file, update theExtIdProvider
to have a custom enum value (DEMO
):
enum ExtIdProvider {
EXT_UNUSED = 0;
reserved 1 to 100; // Pragma supported Identity Providers
// === EXTENSIONS (101+) ===
DEMO = 101; // Add this
}
- Run the following command to build the protos:
make ext-protos
.
Create an Identity Provider Plugin class #
To create a custom Identity Plugin implement this interface:
interface IdentityProviderPlugin: IdentityProvider, PragmaServicePlugin
Below is an example class implementing this interface:
// DemoIdentityProviderPlugin.kt
class DemoIdentityProviderPlugin(
override val service: Service,
override val contentDataNodeService: ContentDataNodeService
) : ConfigurablePlugin<DemoIdProviderConfig>, IdentityProviderPlugin {
override fun getType() = ExtIdProvider.DEMO_VALUE
override var canAuthenticate: Boolean = true
override var separator: String = ""
private lateinit var config: DemoIdProviderConfig
private var httpClient:
TimedHttpClient = TimedHttpClient("demo.outgoing.http", service)
constructor(
service: Service,
contentDataNodeService: ContentDataNodeService,
timedHttpClient: TimedHttpClient
) : this(service, contentDataNodeService) {
this.httpClient = timedHttpClient
}
override suspend fun onConfigChanged(config: DemoIdProviderConfig) {
this.config = config
}
// Code to always validate providerToken
override suspend fun validate(providerToken: String): IdProviderAccount {
return IdProviderAccount(
ExtIdProvider.DEMO_VALUE,
id_provider_user_id,
id_provider_display_name,
id_provider_discriminator
)
}
// Code to return information Portal needs to use as Identity Provider
override fun getPublicInfo(): Map<String, String> {
return mapOf(
"clientId" to config.clientId,
"showPortalLoginButton" to config.showPortalLoginButton.toString(),
// any additional required fields
)
}
}
getPublicInfo
should not return any secret or sensitive information.
Create a config class #
Below is an example of a plugin config:
// DemoIdentityProviderConfig.kt
import pragma.config.ConfigBackendModeFactory
import pragma.settings.BackendType
class DemoIdProviderConfig private constructor(type: BackendType) :
IdentityProviderPluginConfig<DemoIdProviderConfig>(type) {
override val description = "Demo Id Provider configuration."
var clientSecret by types.encryptedString("your app's secret key")
var clientId by types.string("your app's public id")
companion object : ConfigBackendModeFactory<DemoIdProviderConfig> {
override fun getFor(type: BackendType) : DemoIdProviderConfig {
return DemoIdProviderConfig(type)
}
}
}
Any configurable settings for this plugin can be added in the config class.
You can find a list of the supported types in theinner class Types
which is found in the fileplatform/2-pragma/core/src/main/kotlin/pragma/config/ConfigObject.kt
.
Configure the identity provider #
To enable and configure your custom Identity Provider Plugin, you’ll need to add the plugin config to your chosen Pragma Engine YAML
. For local development this would be in platform/5-ext/config/local-dev.yml
.
Below is an example of enabling your custom identity provider:
social:
pluginConfigs:
AccountService.identityProviderPlugins:
plugins:
Demo:
class: "pragma.account.DemoIdentityProviderPlugin"
config:
clientId: "your-client-id"
clientSecret: "encrypted-string-for-your-client-secret"
[optional] Set up Portal login #
To enable Portal login you’ll need to do the following:
- Go to the
5-ext/web/portal/src/authProviders
directory. - Create a
demo
folder. - In the
demo
directory, create a file nameddemo.js
.
import SignInDemo from './SignInDemo.vue.js'
import { demoAuthStep1_InitAndRedirect } from './demoAuth.js'
export default {
id: 'DEMO',
name: 'Demo',
iconName: 'demo-css-icon',
onSignInButtonClick: ({ router }) => {
demoAuthStep1_InitAndRedirect()
},
// these components will be rendered inside the usual Signin box
registeredRoutes: [
{
name: 'SignInDemo',
path: '/signin-demo',
component: SignInDemo
}
],
// should return true if the url is forcely changed
routerBeforeHook: ({ router }) => {
return false
}
}
- Create a file named
demoAuth.js
. This is where you define logic to communicate with your custom identity provider.
const { store } = pragma
const { utils } = pragma
const { signIn, getIdProviderConfig } = pragma.auth
export const REDIRECT_PATH = '/signin-demo'
export const authenticateDemo = async token =>
await signIn({
providerId: 'DEMO',
providerToken: token
})
export const demoAuthStep1_InitAndRedirect = () => {
const config = getIdProviderConfig('DEMO')
...
Code to request auth page of provider
...
utils.goToUrl(demoAuthorizeUrl)
}
export const demoAuthStep2_GetTokenAndAuthenticate = async code => {
const token = await getDemoAuthToken(code)
if (!token) return { ok: false, error: 'Invalid code.' }
return await authenticateDemo(token)
}
const getDemoAuthToken = async code => {
...
Code to request auth token using oauth code
...
}
export default {
REDIRECT_PATH,
authenticateDemo,
demoAuthStep1_InitAndRedirect,
demoAuthStep2_GetTokenAndAuthenticate,
getDemoAuthToken
}