auth0 - invalid user_metadata type - reactjs

I am using Auth0 for user management and I am trying to add some default user_metadata on signup. My signup code looks like this:
// signs a user up
signup(email, password, callback, metadata){
const defaultVals = {
app_complete: false,
app_decision: 'unknown',
app_term: this.getAppTerm(),
nickname: '',
middle_initial: '',
current_age: '10',
}
const meta = Object.assign({}, defaultVals, metadata);
console.log(meta);
this.auth0.redirect.signupAndLogin({
connection: 'Username-Password-Authentication',
email,
password,
user_metadata: meta,
}, function(err, authResult) {
if (err != undefined) {
callback(err);
console.log(err);
return;
}
});}
The problem is, whenever I try to pass the user_metadata attribute app_complete: false, the API returns an error saying that the data type false is invalid, and that only strings are allowed. Error message: invalid user_metadata.app_complete type (only strings are allowed).
I know JSON allows for types other than strings, because I can change this to false through the Auth0 user dashboard. Why is the signup method for the auth0 WebAuth object returning this error?
Thanks.

This is because for signup in the user_metadata you can provide only strings as values.
The user metadata to be associated with the user. If set, the field must be an object containing no more than ten properties. Property names can have a maximum of 100 characters, and property values must be strings of no more than 500 characters.
PATCH on the other hand supports full JSON types in user_metadata.
Here is the reference in the github auth0-js issue

You can pass the user_metadata in the signupAndLogin() (/dbconnections/signup endpoint) method. However, it has some restriction as described below.
The user metadata to be associated with the user. If set, the field
must be an object containing no more than ten properties. Property
names can have a maximum of 100 characters, and property values must
be strings of no more than 500 characters.
To solve the issue, pass the property value as string.
Alternatively, instead of passing the user_metadata in the request, It is possible to use auth0 rules and update the user_metadata on the first login.
function (user, context, callback) {
var count = context.stats && context.stats.loginsCount ? context.stats.loginsCount : 0;
if (count > 1) {
return callback(null, user, context);
}
//update metadata
// https://auth0.com/docs/rules/guides/metadata#update-user_metadata
callback(null, user, context);
}

Related

Set character minimum on Okta Sign in Widget (with React)

I am using the Okta Sign in Widget with okta/okta-react and okta/okta-signin-widget.
Our database is saving users and requires at least 2 characters for their first name, last name, and organization. I would like to set a minimum character limit when a user enters a single character. Currently, if a user enters a single character for any of those fields, Okta will register the account and not display any errors. I would like to create a custom error to display under the textfield, ex: This field must be at least 2 characters long.
I have looked into Okta's i18n properties and do not see any direct reference to the aforementioned fields.
My Widget snippet:
const widget = new OktaSignIn({
baseUrl: issuer.split("/oauth2")[0],
clientId,
redirectUri,
logo: `${process.env.PUBLIC_URL}/tempLogo.png`,
i18n: {
en: {
"primaryauth.title": "Sign in to OceanTracking",
// This is where I would like to enter the character limits
},
},
authParams: {
issuer,
scopes,
},
useInteractionCodeFlow: useInteractionCode,
features: {
registration: true,
},
onSuccess: onSuccess,
onError: onError,
})
Example of the type of error I'd like to display ("This field cannot be left blank"):
Thank you so much.

Self reference type in GraphQL [duplicate]

Hi I am trying to learn GraphQL language. I have below snippet of code.
// Welcome to Launchpad!
// Log in to edit and save pads, run queries in GraphiQL on the right.
// Click "Download" above to get a zip with a standalone Node.js server.
// See docs and examples at https://github.com/apollographql/awesome-launchpad
// graphql-tools combines a schema string with resolvers.
import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema, using GraphQL schema language
const typeDefs = `
type User {
name: String!
age: Int!
}
type Query {
me: User
}
`;
const user = { name: 'Williams', age: 26};
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
me: (root, args, context) => {
return user;
},
},
};
// Required: Export the GraphQL.js schema object as "schema"
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
// Optional: Export a function to get context from the request. It accepts two
// parameters - headers (lowercased http headers) and secrets (secrets defined
// in secrets section). It must return an object (or a promise resolving to it).
export function context(headers, secrets) {
return {
headers,
secrets,
};
};
// Optional: Export a root value to be passed during execution
// export const rootValue = {};
// Optional: Export a root function, that returns root to be passed
// during execution, accepting headers and secrets. It can return a
// promise. rootFunction takes precedence over rootValue.
// export function rootFunction(headers, secrets) {
// return {
// headers,
// secrets,
// };
// };
Request:
{
me
}
Response:
{
"errors": [
{
"message": "Field \"me\" of type \"User\" must have a selection of subfields. Did you mean \"me { ... }\"?",
"locations": [
{
"line": 4,
"column": 3
}
]
}
]
}
Does anyone know what I am doing wrong ? How to fix it ?
From the docs:
A GraphQL object type has a name and fields, but at some point those
fields have to resolve to some concrete data. That's where the scalar
types come in: they represent the leaves of the query.
GraphQL requires that you construct your queries in a way that only returns concrete data. Each field has to ultimately resolve to one or more scalars (or enums). That means you cannot just request a field that resolves to a type without also indicating which fields of that type you want to get back.
That's what the error message you received is telling you -- you requested a User type, but you didn't tell GraphQL at least one field to get back from that type.
To fix it, just change your request to include name like this:
{
me {
name
}
}
... or age. Or both. You cannot, however, request a specific type and expect GraphQL to provide all the fields for it -- you will always have to provide a selection (one or more) of fields for that type.

Storing CognitoUser in cache make him lose some properties

I'm currently using AWS Cognito in my application.
When a user first connects whit his account, Cognito returns NEW_PASSWORD_REQUIRED as a challenge, which is fine.
I want to redirect to a page where the user can set his new password, so I put the response from Auth.signIn in storage (I tried local storage, session storage and Cache from AWS Amplify) but when I get it back on the other page, it lose some properties and Auth.completeNewPassword returns the error : 'user.completeNewPasswordChallenge is not a function'
Login.js :
try {
var authPromise = Auth.signIn(this.state.email, this.state.password);
authPromise.then((result) => {
console.log(result);
if (result.challengeName === 'NEW_PASSWORD_REQUIRED') {
Cache.setItem("CognitoUser", result);
this.props.history.push("/login/newPassword");
}
else {
this.props.userHasAuthenticated(true);
this.props.history.push("/");
}
});
} catch (e) {
alert(e.message);
this.setState({ isLoading: false });
}
NewPassword.js :
try {
var user = Cache.getItem("CognitoUser");
if (user) {
await Auth.completeNewPassword(user, this.state.newPassword);
this.props.history.push("/");
Cache.removeItem("CognitoUser");
}
} catch (e) {
alert(e.message);
this.setState({ isChanging: false });
}
Any ideas ?
It's javascript so when you write to your localcache and serializes your result into the "CognitoUser" key , it's stored as a a string, which afterwards deserialized will be a plain old Object unaware of the original type before serialization.
Original cause is maybe that your "result" type may expose functions which are not serializable (if not a getter, or if a getter with arguments).
I suggest you to call and store all the data you want into separate keys and re-read them later.
Cache.setItem("CognitoUser", result);
Cache.setItem("CognitoUser-value-1", result.myFunction1("myArg1"));
Cache.setItem("CognitoUser-value-2", result.myFunction2("myArg2"));
// ..
var user = Cache.getItem("CognitoUser");
var myVal1 = Cache.getItem("CognitoUser-value-1");
var myVal2 = Cache.getItem("CognitoUser-value-2");
You can also keep one single key "CognitoUser" in your localStorage if you make all said functions serializable. For instance, extend the type of your result adding prototypes getter functions (no arguments), each calling and returning respective myFunctionX("myArgX") functions, so that they'll appear in the JSON.stringify process.
My work around,
So this problem troubled me for some time. Amplify Cache didn't seem to work and caching username and password is a bad idea, however my work around was just include the username and password in the Require-New-Password form, so I have 4 inputs instead of just newPassword & confirmPassword which now is username, oldPassword, newPassword, and confirmPassword.
https://github.com/aws-amplify/amplify-js/issues/1715#issuecomment-642733574

Getting permission-denied when trying to validate that Firestore user can only post comments under their own UID

I have this code but don't really know where the insufficient security error coming from. I can't seems to figure it out. Could it be from cloudfirestore?
postComment(getBlogId: string, comment: any): Promise<any> {
if (this.currentUser) {
return this.afs.collection('blogs').doc(getBlogId).collection('comments')
.add({
author: {
'_id': this.currentUser.uid,
'firstname': this.currentUser.displayName ? this.currentUser.displayName : this.currentUser.email
},
comment: comment.comment,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
} else {
return Promise.reject(new Error('LogIn to Continue!'));
}
}
This is in the services, but I am getting error when I submit a comment.
And in my component, when iI submit the console, it is giving me the form value and the value of the blog ID.
onSubmit() {
console.log(this.commentForm.value);
console.log(this.blog._id);
this.blogservices.postComment(this.blog._id, this.commentForm.value)
.then(() => {
this.blogservices.getComments(this.blog._id)
.subscribe(comments => this.blog.comments = comments);
});
}
I can't really place it where I am getting the
ERROR Error Uncaught (in promise) FirebaseError [code=permission-denied]: Missing or insufficient permissions FirebaseError: Missing or insufficient permissions.
And this is my firestore rules, which I think is correct because request should match user uid and the resouce attached to the data to be submitted.
match /comments/{comment}{
allow delete: if request.data.user_uid == request.auth.uid;
allow write: if request.resource.data.user_uid == request.auth.uid;
allow read;
}
I have tried posting directly without the user info but getting same error.
You're comparing the UID of the user posting (request.auth.uid), against request.resource.data.user_uid. That means that you'll need to have a field user_uid in the root of your document.
When adding the document however, you do it as this:
.add({
author: {
'_id': this.currentUser.uid,
'firstname': this.currentUser.displayName ? this.currentUser.displayName : this.currentUser.email
},
So the current user's UID is in author._id, which is a different path from what your security rules are looking at. The fix is to make sure both are using the same path. To do that in the security rules would look something like this:
allow write: if request.resource.data.author._id == request.auth.uid;

Is it possible to set the displayName in Firebase for the anonymous authentication

I'm using Firebase's Simple Login through AngularFire with the anonymous login method and I was wondering if there's anyway to set the displayName property of the user object returned from
$scope.loginObj.$login('anonymous').then(function(user) {
user.displayName // This is an empty string
}
I would like to set displayName and save it back in Firebase so that users can be shown as that displayName property. As far as I know you don't actually write anything to Simple Login yourself. It seems like the only time it's written to is if you're using something like email/password authentication and using $scope.loginObj.$createUser('name', 'password')
This isn't possible in the way you describe, however, you can do this to get the same behavior.
$scope.loginObj.$login('anonymous').then(function(user) {
if (!user) return;
$scope.userRef = (new Firebase('<Your Firebase>.firebaseio.com/users/')).child(user.uid);
$scope.userRef.child('displayName').on('value', function (snapshot) {
user.displayName = shapshot.val();
});
});
// Then elsewhere in your code, set the display name and user.displayName will be updated automatically
$scope.userRef.child('displayName').set("DISPLAY NAME");
You can even back this up with simple Security Rules:
{"rules":
"users": {
"$uid" {
".read": true,
".write": "$uid == auth.uid'
}
}
}
This ensures that only a correctly authenticated user can modify their own display name.

Resources