How to include verified email state on users from a custom database with automatic migration in an Auth0 tenant? - auth0

I set up an Auth0 connection database, configured as a custom database with automatic migration turned on. Working great in general. I also successfully set up a rule to force email verification, needed for new signing up users and old users with unverified emails.
Of course, all migrated users are forced to verify their email address; but my biz requirement is that's should happen just for the users who haven't verified their email yet, it's annoying forcing it for old users with already verified emails.
I have a source field for that on the legacy DB, but I'm not sure how I can migrate the user including the verified_email state. My current scripts for the custom database returns these values:
Login script:
login(email, password, callback)
... returning on callback...
{
user_id: ...,
nickname: ...,
email: ...,
}
GetUser script:
getByEmail(email, callback)
... returning on callback...
{
user_id: ...,
nickname: ...,
email: ...,
name: ...,
given_name: ...,
family_name: ...,
}

You just need to edit your Login script to get that property as well and set it to the email_verified key. Just like your script creates a profile that has user_id, email, and other stuff, you just need to add a line to include email_verified in the profile and populate it with the correct data from your legacy datastore. The data you add to the profile when importing is totally up to you.
In this specific case, the value required is a boolean, so it would be just true, without the quotes.

Related

Getstream.io throws exception when using the "to" field

I have two flat Feed Groups, main, the primary news feed, and main_topics.
I can make a post to either one successfully.
But when I try to 'cc' the other using the to field, like, to: ["main_topics:donuts"] I get:
code: 17
detail: "You do not have permission to do this, you got this error because there are no policies allowing this request on this application. Please consult the documentation https://getstream.io/docs/"
duration: "0.16ms"
exception: "NotAllowedException"
status_code: 403
Log:
The request didn't have the right permissions or autorization. Please check our docs about how to sign requests.
We're generating user tokens server-side, and the token works to read and write to both groups without to.
// on server
stream_client.user(user.user_id).create({
name: user.name,
username: user.username,
});
Post body:
actor: "SU:5f40650ad9b60a00370686d7"
attachments: {images: [], files: []}
foreign_id: "post:1598391531232"
object: "Newsfeed"
text: "Yum #donuts"
time: "2020-08-25T14:38:51.232"
to: ["main_topics:donuts", "main_topics:all"]
verb: "post"
The docs show an example with to: ['team:barcelona', 'match:1'], and say you need to create the feed groups in the panel, but mention nothing about setting up specific permissions to use this feature.
Any idea why this would happen? Note that I'm trying to create the new topics (donuts, all) which don't exist when this post is made. However, the docs don't specify that feeds need to be explicitly created first - maybe that's the missing piece?
If you haven’t already tried creating the feed first, then try that. Besides that, the default permissions restrict a user from posting on another’s feed. I think it’s acceptable to do this if it’s a notification feed but not user or timeline.
You can email the getstream support to change the default permissions because these are not manageable from the dashboard.
Or you can do this call server side as an admin permissions.

Express REST API with JWT and Routes

I am trying to create an Express API with JWT authentication.
However, I was wondering how to best allow users to only access their own resources.
For example if there is a user with id 1 and each user has a list of books in the database:
The id is already part of the JWT Token but commonly there would be a request to something like /users/1/books to get all of the books belonging to user 1.
Would my routes typically still look like this and I would just check the id in the token is the same the request is made for, or is there any other/simpler way?
Thank you for your help!
You can define, some access rights permissions base on the user role or id.
Example: roles : {root, admin, staff}
Then, in your routes you can have some checking whether this user have the permission to access the functions or you can do in the controller level to check the access rights.
You need to define model relations between User, UserModel. In your case as I understand you need to have the relations between UserModel and BooksModels.
UserModel hasMany BooksModel
When you call findOne() to retrieve specific user's data, you can just define include: 'aliasModelName', to retrieve the users related book data.
With this way, you can only have 1endpoint users/:id to retrieve users data and book data. It depends on what you really want, you can also have an endpoint users/:id/books to get all books that belongs to this user.
Your model definition will then become
BooksModel belongsTo UserModel
If you use hasMany you can get all the results that you need in just one query.
Hope this helps!
When user sends the login credentials, you check database if the email exists, if yes then you check if the password matches. If user successfully signins you create the token.
const token = jwt.sign({ _id: user._id, email: user.email }, "this-is-secret", {
expiresIn: "1h",
});
this token is sent to the browser, whenever user make requests, it manually attachs this token to the req, and sends the request to your server. You check if the token is valid, by using the secret key (in this case "this-is-secret").
const decodedToken = jwt.verify(token, "this-is-secret")
req.userId = decodedToken.userId;
now "userId" is attached to the req object. Now when you fetch the data from database, the items that you are fetching, you write a query that (implementation depends on which database you are using)
book.userId=req.userId

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.

getProfileUserInfo is returning an empty email and an empty id [duplicate]

I am trying to get email and id of user through chrome identity api.
I am doing this
chrome.identity.getProfileUserInfo(function(userinfo){
console.log("userinfo",userinfo);
email=userinfo.email;
uniqueId=userinfo.id;
});
I have specified identity permission and have added https://www.googleapis.com/auth/userinfo.email in scopes.
User is logged in through chrome.identity.getAuthToken and I have access token.
console.log("userinfo",userinfo); returns userinfo Object {email: "", id: ""}
The getProfileUserInfo documentation says:
email: Empty if the user is not signed in or the identity.email manifest permission is not specified.
id: Empty if the user is not signed in or (in M41+) the identity.email manifest permission is not specified.
Edit manifest.json to include both permissions:
"permissions": ["identity", "identity.email"]
In addition to having the identity.email permission, as posted in the related Chrome issue for this subject, users must have their profile syncing turned on under chrome://settings -> People -> Sync for their ID and email to show up using chrome.identity.getProfileUserInfo.