How to cloudform cognito user pool authentication provider with custom mapping - amazon-cognito

I've successfully cloudformed a cognito identity-pool and could not see how I add the custom mappings to the "Cognito" "Authentication Providers" in cloudformation.
Inside the Cognito Authentication Provider on the console, there is a dropdown where I manually have to select "Use custom mappings" and then I can manually add the mappings to my custom user attributes. However, I need to be able to cloudform this and am struggling to find the correct place for it.
The user pool that goes along with this identity pool has "SupportedIdentityProviders" set to "COGNITO"
Update
I can get a list of identity providers by running ...
aws cognito-identity list-identities --max-results 2 --identity-pool-id xx-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
and this returns me
{
"IdentityPoolId": "xx-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
"Identities": [
{
"IdentityId": "yy-yyyy-y:yyyyyyyy-yyyy-yyyy-yyyyyyyyyy",
"Logins": [
"cognito-idp.eu-west-2.amazonaws.com/eu-west-2_tFT6FBwIO"
],
"CreationDate": "2021-11-15T12:38:48.249000+00:00",
"LastModifiedDate": "2021-11-15T12:38:48.263000+00:00"
}
]
}
using the "Logins" information I can now run...
aws cognito-identity get-principal-tag-attribute-map --identity-pool-id xx-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx --identity-provider-name "cognito-idp.eu-west-2.amazonaws.com/eu-west-2_tFT6FBwIO"
which returns
{
"IdentityPoolId": "xx-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
"IdentityProviderName": "cognito-idp.eu-west-2.amazonaws.com/eu-west-2_tFT6FBwIO",
"UseDefaults": false,
"PrincipalTags": {
"attr_x": "custom:attr_x",
"attr_y": "custom:attr_y",
"attr_z": "custom:attr_z"
}
}
However, I still don't know how to setup this mapping via cloudformation...
Regards
Mark.

Setting PrincipalTag attribute mappings is not yet supported in CloudFormation but, according to the CloudFormation roadmap, will be supported soon.
In the meantime, you would have to create a CloudFormation Custom Resource or Resource Provider to achieve this.

Related

Keycloak - how to allow linking accounts without registration

I am managing a Keycloak realm with only a single, fully-trusted external IdP added that is intended to be the default authentication mechanism for users.
I do not want to allow user to register, i.e. I want to manually create a local Keycloak user, and that user should then be allowed to link his external IdP account to the pre-existing Keycloak account, having the email address as common identifier. Users with access to the external IdP but without an existing Keycloak account should not be allowed to connect.
I tried the following First Broker Login settings, but whenever a user tries to login, he gets an error message (code: invalid_user_credentials).
Do you have any idea what my mistake might be?
Looks like they integrated this feature in version 4.5.0.
See automatic account link docs.
Basically you need to create a new flow and add 2 alternative executions:
Create User If Unique
Automatically Link Brokered Account
According to the doc: https://www.keycloak.org/docs/latest/server_admin/index.html#detect-existing-user-first-login-flow, you must create a new flow like this:
et voilà :)
As per this discussion:
https://keycloak.discourse.group/t/link-idp-to-existing-user/1094/5
It’s a bug in keycloak and they seem to be a reluctant to fix it for
whatever reason. I have very few users so I solved it by manually
querying the idp for the information keycloak uses and then copying it
into the relevant fields in the UI. So there is no sign up process for
my users I just make them myself. Obviously that’s a poor solution
though, what we really need is someone to take over that PR and
persuade the maintainers to merge it.
This is the PR: https://github.com/keycloak/keycloak/pull/6282
As it is described in this GitHub issue response the solution is to use a JavaScript authenticator that handles this.
In order to do so, you need to do the folowing:
Enable [custom authenticators using JavaScript in your server[(https://www.keycloak.org/docs/latest/server_installation/#profiles) by https://stackoverflow.com/a/63274532/550222creating a file profile.properties in your configuration directory that contains the following:
feature.scripts=enabled
Create the custom authenticator. You have to create a JAR file (essentially a ZIP file) with the following structure:
META-INF/keycloak-scripts.json
auth-user-must-exist.js
The content of the files are in this Gist, but I am including them here as well:
META-INF/keycloak-scripts.json:
{
"authenticators": [
{
"name": "User must exists",
"fileName": "auth-user-must-exists.js",
"description": "User must exists"
}
]
}
auth-user-must-exist.js:
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError")
ServicesLogger = Java.type("org.keycloak.services.ServicesLogger")
AbstractIdpAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator")
IdpCreateUserIfUniqueAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator")
var IdpUserMustExists = Java.extend(IdpCreateUserIfUniqueAuthenticator)
function authenticate(context) {
var auth = new IdpUserMustExists() {
authenticateImpl: function(context, serializedCtx, brokerContext) {
var parent = Java.super(auth)
var session = context.getSession()
var realm = context.getRealm()
var authSession = context.getAuthenticationSession()
if (authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) != null) {
context.attempted()
return
}
var username = parent.getUsername(context, serializedCtx, brokerContext)
if (username == null) {
ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username")
authSession.setAuthNote(AbstractIdpAuthenticator.ENFORCE_UPDATE_PROFILE, "true")
context.resetFlow()
return
}
var duplication = parent.checkExistingUser(context, username, serializedCtx, brokerContext)
if (duplication == null) {
LOG.info("user not found " + username)
context.failure(AuthenticationFlowError.INVALID_USER)
return
} else {
authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, duplication.serialize())
context.attempted()
}
}
}
auth.authenticate(context)
}
Then, you can define as follows:
User Must Exist -> ALTERNATIVE
Automatically Set Existing User -> ALTERNATIVE
Honestly i am surprised by the keycloak auto creating behavior. I tried to add new Authentication flow as descibed here https://www.keycloak.org/docs/latest/server_admin/index.html#automatically-link-existing-first-login-flow
My flow :
1 - Create User If Unique [ALTERNATIVE]
2 - Automatically Link Brokered Account [ALTERNATIVE]
My use case : Authenticating users from Github ( Github as IDP )
Result : when a github user logon with an existing "username" keycloak links the github account to my local user ( based on his username ). I expected using his email instead of username.

slashDB accessing a database via POST request and using APIkey yields 403 error

Question about security for POST method of HTTP:
I made a user called "MyAPP":
{
"userdef": [
"view",
"create"
],
"api_key": "dzn8k7hj2sdgddlvymfmefh1k2ddjl05",
"user_id": "MyAPP",
"name": "MyAPP",
"creator": "admin",
"edit": [],
"dbdef": [
"view",
"create"
],
"querydef": [
"view",
"create"
],
"databases": {
"Gaming": {
"dbuser": "mydbuser_here",
"dbpass": "mypass_here"
}
},
"password":
"$6$rounds=665736$x/Xp0k6Nj.5qzuM5$G.3w6Py1s.xZ83RHDU55qonNMpJe4Le8nD8PqjYKoOtgbab7T22knJPqwHspoT6BQxp.5gieLFuD0SdD9dyvi/",
"email": "",
"view": []
}
Then I wanted to issue a POST in order to execute a SQL Pass-thru
such as this:
http:///query/InsertBestScore/Score/99/ScreenName/GollyGolly.xml?apikey=dzn8k7hj2sdgddlvymfmefh1k2ddjl05
Where I built a query and named it "InsertBestScore":
insert into Gaming.Leaderboard
(ScreenName, Score)
values
(:ScreenName, :Score);
If I run this via POSTMAN using the POST method:
... then I get an access, 403 :
<?xml version="1.0" encoding="utf-8"?>
<SlashDB>
<http_code>403</http_code>
<description>Access was denied to this resource. Please log in with your username/password or resend your request with a valid API key.</description>
<url_template>/query/InsertBestScore/Score/{Score}/ScreenName/{ScreenName}.xml</url_template>
</SlashDB>
Also, I would be calling this POST (or PUT) request from an application, in my case a Python program running from within a AWS Lambda Function.
Now, I came across this in the documentation:
Two parameters API key
SlashDB also allows a two parameters credentials in this authentication method - app id and api key. This may come handy when integrating with API management systems like 3Scale. By default header and query string argument would be:
• appid - identifies certain application
• apikey - secret for the application
Request with API key in header - Access granted
... however in the example above, I don't see where the appid comes into play.
Can you tell me how one would call the SlashDB endpoint and pass a APIkey and assure that the userid is known as MyAPP.
So, to sum up, the Documentation mentions:
• Another application utilizes an API key to authenticate, which is sent with every request. The application is recognized as SlashDB user App2, which uses database login db_admin. Effectively this application can SELECT, UPDATE, INSERT and DELETE data.
So I want to actually, do just what is in that bullet: Identify myself as the user (instead of App2, I'm user MyAPP), and then use the dbuser and dbpass that was assigned to access that "Gaming" database.
Idea?
Make sure you've given user MyAPP permission to execute the query.
To do so:
login as admin,
go to Configure -> Queries,
open your query definition,
update field Execute. It accepts comma separated user ids.
OK, there are really two questions here:
Why was access denied?
What is the appid and how to use it.
Ad. 1: There are two authorization barriers that the request has to clear.
The first one is imposed by SlashDB in that the user executing the query must be listed in the Execute field on the query definition screen. This is done under Configure -> Queries -> "edit" button on your query.
The second barrier is imposed by the database. The SlashDB user who is executing your POST request must be mapped to a physical database user with INSERT privileges to the Gaming.Leaderboard table. It goes without saying that this database user must be associated with the database schema in which the table exists.
Ad. 2. To enable the appid the user api key must be composed out of two parts separated by colon ":". The first part will be interpreted as the appid and the second will be the apikey.
To do that, use Configuration -> Users -> 'edit' button for the user in question. Then simply add a colon at the beginning of the API key and type in your desired appid to the left of the colon. The app will have to supply both keys to execute the request. Note that the names of those keys (i.e. appid) are configurable in /etc/slashdb/slashdb.ini.
The reasoning behind this feature is to facilitate API Management platforms, which can help with key management, especially when API will be exposed to third party developers.

AWS Cognito Federated Identity Pool Custom Authentication Provider Sing out / logout issue

I am using a nodejs lamdas to get authentication tokens from AWS Cognito and in the front end code I am using the "aws-sdk": "^2.74.0" javascript / typescript sdk :
var creds = new AWS.CognitoIdentityCredentials({
IdentityPoolId: environment.identityPoolId
})
AWS.config.update({
region: environment.region,
credentials: creds
});
var lambda = new AWS.Lambda();
when I sign the token and identity id to my AWS.CognitoIdentityCredentials.params the following way :
creds.params['IdentityId'] = output.identityId;
creds.params['Logins'] = {};
creds.params['Logins']['cognito-identity.amazonaws.com'] = output.token;
creds.expired = true;
I am able to get the following lamda.invoke calls to use authenticated role arn configured for my federated identity pool.
The issue I am having is when I try to sign the user out. I read many forums posts but nobody seem to have a clear explanation on this. I tried using the following in my front end logout function which didn't help:
creds.clearCachedId();
creds.refreshPromise();
any examples showing how the javascript aws-sdk would clear the session/authentication information and switch back to unauthenticated user role arn or logout user and update the config so that next call a AWS service ( lambda.invoke in my case ) would use the unauthenticated role arn instead of trying to use the authenticated role. So it seems Cognito is not aware of the sigout, or I am missing the call to make it aware. I was hoping creds.clearCachedId() would do it but apparently not.
Well it turns out I needed to clear the creds.params manually :
creds.params['IdentityId'] = null;
creds.params['Logins'] = null;
I would think the below would do it, but apparently not.
creds.clearCachedId();
creds.refreshPromise();

Creating users in dream factory

I was using dreamfactory and was trying to create users with different roles and use those to send requests however those users aren't working for me. The admin users are however. I don't understand why that is.
Below are the steps I've taken;
In the admin console, click the Roles tab then click Create in the left sidebar.
Enter a name for the role and check the Active box.
Go to the Access tab.
Add a new entry under Service Access (you can make it more restrictive later).
set Service = All
set Component = *
check all HTTP verbs under Access
set Requester = API
Click Create Role.
Click the Services tab, then edit the user service. Go to Config and enable
Allow Open Registration.
Set the Open Reg Role Id to the name of the role you just created.
Make sure Open Reg Email Service Id is blank, so that new users can register without email confirmation.
Save changes.
Then I registered the user incognito mode
I used a google extension rest client called Postman to make the request
In postman I used Basic Auth entered the username and password and sent the request
{
"error": {
"context": null,
"message": "Access Forbidden.",
"code": 403
}
...
Above is the result I got when I use the user I just registered. However when I use the admin username and password, I get the following response.
Status 200 OK
{"resource": [{"name": "_schema"},{"name": "_table"},{"name": "_proc"},{"name":"_func"}]}
which is the result I want for the other users.
Please let me know what I did wrong so I can fix it. Thanks again for all your help
I have configured my system the same and will walk through the steps. This is done on the 06 Oct 2015 commit of DreamFactory 2.0 beta.
First, to register a user:
POST /api/v2/user/register
X-DreamFactory-Api-Key: 0d430cc287b5dd88b0f615d2bb69d84ba7201e8d4214fee2eabcc96bcd937d7d
{
"email": "user#domain.com",
"first_name": "Basic",
"last_name": "User",
"display_name": "Basic User",
"new_password": "password"
}
Response: {"success":true}
Next, to login as that user:
POST /api/v2/user/session
X-DreamFactory-Api-Key: 0d430cc287b5dd88b0f615d2bb69d84ba7201e8d4214fee2eabcc96bcd937d7d
{
"email": "user#domain.com",
"password": "password"
}
Response:
{
"session_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjMsInVzZXJfaWQiOjMsImZvcmV2ZXIiOmZhbHNlLCJpc3MiOiJodHRwczpcL1wvZGVtby5lbnRlcnByaXNlLmRyZWFtZmFjdG9yeS5jb21cL2FwaVwvdjJcL3VzZXJcL3Nlc3Npb24iLCJpYXQiOiIxNDQ0Njc1NDU0IiwiZXhwIjoiMTQ0NDY3OTA1NCIsIm5iZiI6IjE0NDQ2NzU0NTQiLCJqdGkiOiIyMjFiYTg5M2E2YTc4OThlNDdlZGI4MGY5YjVkY2M2ZCJ9.V8y2B_2vlh8QdFhRqyuSG8SbfIVe8g71RvNjKoW2Bi8",
"session_id":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjMsInVzZXJfaWQiOjMsImZvcmV2ZXIiOmZhbHNlLCJpc3MiOiJodHRwczpcL1wvZGVtby5lbnRlcnByaXNlLmRyZWFtZmFjdG9yeS5jb21cL2FwaVwvdjJcL3VzZXJcL3Nlc3Npb24iLCJpYXQiOiIxNDQ0Njc1NDU0IiwiZXhwIjoiMTQ0NDY3OTA1NCIsIm5iZiI6IjE0NDQ2NzU0NTQiLCJqdGkiOiIyMjFiYTg5M2E2YTc4OThlNDdlZGI4MGY5YjVkY2M2ZCJ9.V8y2B_2vlh8QdFhRqyuSG8SbfIVe8g71RvNjKoW2Bi8",
"id":3,
"name":"Basic User",
"first_name":"Basic",
"last_name":"User",
"email":"user#domain.com",
"is_sys_admin":false,
"last_login_date":"2015-10-12 18:44:14",
"host":"console",
"role":"basic",
"role_id":1
}
Since you tried using basic auth, I did too:
GET /api/v2/db
X-DreamFactory-Api-Key: 0d430cc287b5dd88b0f615d2bb69d84ba7201e8d4214fee2eabcc96bcd937d7d
Authorization: Basic dXNlckBkb21haW4uY29tOnBhc3N3b3Jk
Response:
{
"resource":
[
{"name":"_schema"},
{"name":"_table"},
{"name":"_proc"},
{"name":"_func"}
]
}
Docs for DreamFactory 2.0: http://wiki.dreamfactory.com/DreamFactory/API
In-depth docs for each possible API call may be found in the API Docs tab of a DreamFactory instance. API Docs are automatically generated and self-documenting, using Swagger UI.

AccessDeniedException when writing into JCR (JackRabbit) [Magnolia]

I wrote a request filter for geoIP localization. It works the way that I request an external service for the localization and then write the information into JCR, into a dedicated workspace for caching/storage.
On the author instance this works, but on the public instance I constantly get a AccessDeniedException. I probably need to authenticate with the JCR, and I tried that too, using the crendentials from the magnolia.properties file:
magnolia.connection.jcr.userId = username
magnolia.connection.jcr.password = password
And this code for authentication:
Session session = MgnlContext.getJCRSession(WORKSPACE_IP_ADDRESSES);
session.impersonate(new SimpleCredentials("username", "password".toCharArray()));
I have the this xml to bootstrap the filter, and a FilterOrdering Task, configured as follows:
tasks.add(new FilterOrderingTask("geoIp", new String[] { "contentType", "login", "logout", "csrfSecurity",
"range", "cache", "virtualURI" }));
What am I missing?
What would be the proper to write into the JCR in Magnolia on the public instance?
Yeah, that could not work :D
Is your filter configured in Magnolia's filter chain or directly in web.xml? It needs to live in filter chain and it needs to be configured somewhere down the chain after the security filters so that user is already authenticated.
Then you can simply call MgnlContext.getJCRSession("workspace_name") to get access to repo and do whatever you need.
HTH,
Jan