First time working with Firebase on a new project and I'm getting a permission denied message when writing an activity event when I include a validation rule.
The validation rule looks like:
"activity": {
".read": "auth != null",
".write": "auth != null",
".validate": "newData.hasChildren(['user'])",
".indexOn": ["when"]
}
On a new activity event, I push a new entry, grab the token ID and make it (for now) part of the data being pushed. When watching this in debug (using a custom token authentication system) this is what I see. The json pushed has a "user" entry that is the GUID of the auth user so I'm not sure why it's failing. I spaced out the json text.
utility.js (line 1675)
FIREBASE: Attempt to write {
"id":"-K4oomuOpaY4K2aGUYZA",
"imp":false,
"text":"xxx.",
**"user":"0648480c-xxx"**,
"when":1449363973059
} to /activity/## with auth={"uid":"0648480c-xxx","name":"Greg Merideth"}
/activity:.write: "auth != null" => true
/activity:.validate: "newData.hasChildren(['user'])" => false
FIREBASE: Validation failed. firebase.js (line 195)
FIREBASE: Write was denied firebase.js (line 195)
I even tried changing the rule to ".validate": "newData.hasChild('user')" with the same end result.
Is newData looking at the inbound packet or my "auth" packet?
Update (from the comments)
The addition of a new item calls a function passing in the fbActivity handler which then calls:
var message = fbActivity.push({
id: user.fn(),
text: t.val(),
imp: false,
user: user.uid(),
when: new Date().getTime()}
To push the new entry. We're not using the fb.timestamp as our server runs 3 seconds behind fb's so our time stamps come out weird.
I'm guessing that you're calling push() to add a new child under activity. In that case, your rules are missing the extra level that is generated by push():
"activity": {
"$activityid": {
".read": "auth != null",
".write": "auth != null",
".validate": "newData.hasChildren(['user'])",
".indexOn": ["when"]
}
}
If that is the case, please take time to read the Firebase security guide, which explains this and many other useful bits about the language.
Related
I'm using serverless stack, now attempting to add a Lambda Custom Authenticator to validate authorization tokens with Auth0 and add custom data to my request context when the authentication passes.
Everything works mostly fine at this point, except for when I cache the Authenticator response for the same token.
I'm using a 5-second cache for development. The first request with a valid token goes through as it should. The next requests in the 5-second window fail with a mysterious 500 error without ever reaching my code.
Authorizer configuration
// MyStack.ts
const authorizer = new sst.Function(this, "AuthorizerFunction", {
handler: "src/services/Auth/handler.handler",
});
const api = new sst.Api(this, "MarketplaceApi", {
defaultAuthorizationType: sst.ApiAuthorizationType.CUSTOM,
defaultAuthorizer: new HttpLambdaAuthorizer("Authorizer", authorizer, {
authorizerName: "LambdaAuthorizer",
resultsCacheTtl: Duration.seconds(5), // <-- this is the cache config
}),
routes: {
"ANY /{proxy+}": "APIGateway.handler",
},
});
Authorizer handler
const handler = async (event: APIGatewayAuthorizerEvent): Promise<APIGatewayAuthorizerResult> => {
// Authenticates with Auth0 and serializes context data I wanna
// forward to the underlying service
const authentication = await authenticate(event);
const context = packAuthorizerContext(authentication.value);
const result: APIGatewayAuthorizerResult = {
principalId: authentication.value?.id || "unknown",
policyDocument: buildPolicy(authentication.isSuccess ? "Allow" : "Deny", event.methodArn),
context, // context has the following shape:
// {
// info: {
// id: string,
// marketplaceId: string,
// roles: string,
// permissions: string
// }
// }
};
return result;
};
CloudWatch logs
☝️ Every uncached request succeeds, with status code 200, an integration ID and everything, as it's supposed to. Every other request during the 5-second cache fails with 500 error code and no integration ID, meaning it doesn't reach my code.
Any tips?
Update
I just found this in an api-gateway.d.ts #types file (attention to the comments, please):
// Poorly documented, but API Gateway will just fail internally if
// the context type does not match this.
// Note that although non-string types will be accepted, they will be
// coerced to strings on the other side.
export interface APIGatewayAuthorizerResultContext {
[name: string]: string | number | boolean | null | undefined;
}
And I did have this problem before I could get the Authorizer to work in the first place. I had my roles and permissions properties as string arrays, and I had to transform them to plain strings. Then it worked.
Lo and behold, I just ran a test right now, removing the context information I was returning for successfully validated tokens and now the cache is working 😔 every request succeeds, but I do need my context information...
Maybe there's a max length for the context object? Please let me know of any restrictions on the context object. As the #types file states, that thing is poorly documented. This is the docs I know about.
The issue is that none of the context object values may contain "special" characters.
Your context object must be something like:
"context": {
"someString": "value",
"someNumber": 1,
"someBool": true
},
You cannot set a JSON object or array as a valid value of any key in the context map. The only valid value types are string, number and boolean.
In my case, though, I needed to send a string array.
I tried to get around the type restriction by JSON-serializing the array, which produced "[\"valueA\",\"valueB\"]" and, for some reason, AWS didn't like it.
TL;DR
What solved my problem was using myArray.join(",") instead of JSON.stringify(myArray)
I'm trying to implement the EPIC FHIR SMART Backend Services (Backend OAuth 2.0)
on go programming language.
I've created my dev account, uploaded the public key there, and selecting the backend system as the application audience.
I'm pretty sure my jwt token is correct. I've inspected it on jwt.io, the signature is correct. However, I always get this error:
{ "error": "invalid_client", "error_description": null }
I've tried other possible solutions as well such as:
ensuring the expiration date within the jet claim is below 5 minutes
placing the payload in the body with the correct content type, which is application/x-www-form-urlencoded
ensuring to use the sandbox client_id
using the correct jwt sign in method (RS384)
What should I do to resolve this issue?
Btw, I also saw several discussions on the google groups saying that it's worth to wait for one or two days after the dev account is created.
Below is my code. Appreciate the help!
var (
oauth2TokenUrl = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token"
sandboxClientID = "..."
privateKey = "..."
)
// load private key
signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
So(err, ShouldBeNil)
// construct jwt claims
now := time.Now()
claims := jwt.MapClaims{
"iss": sandboxClientID,
"sub": sandboxClientID,
"aud": oauth2TokenUrl,
"jti": uuid.New().String(), // fill with reference id
"exp": now.Add(1 * time.Minute).Unix(), // cannot be more than 5 minutes!
}
log.Info(" => claims:", utility.ToJsonString(claims))
// generate signed token using private key with RS384 algorithm
alg := jwt.SigningMethodRS384
signedToken, err := jwt.NewWithClaims(alg, claims).SignedString(signKey)
So(err, ShouldBeNil)
log.Info(" => signed token", signedToken)
// prepare api call payload
payload := map[string]string{
"grant_type": "client_credentials",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": signedToken,
}
// dispatch the api call
req := resty.New().
R().
EnableTrace().
SetFormData(payload)
res, err := req.Post(oauth2TokenUrl)
So(err, ShouldBeNil)
log.Info(" => response status:", res.StatusCode())
log.Info(" => response header:", res.Header())
log.Info(" => response body:", string(res.Body()))
// parse response
resBody := make(map[string]interface{})
err = json.Unmarshal(res.Body(), &resBody)
So(err, ShouldBeNil)
Fantastic, I got it working now.
The solution is simply waiting! it was confusing because I can't find any explanation about this on the doc, and also the error message is not quite friendly.
in summary, after creating dev app and the public key is uploaded there, we have to wait for a few hours/days, and then the credentials will eventually be usable.
The waiting part is applied to both open epic and app orchard dev accounts.
It seems that Epic has some kind of synchronising mechanism which runs once a day. So waiting after account create is the only solution. Please also note that, in app settings after Endpoint URI change you also have to wait some time.
Error { "error": "invalid_client", "error_description": null } also shows up when redirect_uri param is set to something like localhost:3000.
I encountered this problem too. In my case, I was using "Patients" as the "Application Audience" selected for the Epic SMART on FHIR app. I was able to successfully obtain an authorization code on the test server, but when I attempted to exchange it for an access token I received "invalid_client" error message.
The mistake I made is that the redirect_uri in the HTTP POST must be an absolute URL and must match a redirect URI you have specified for your app. If the redirect URI is invalid, the resulting error message will say "invalid client" (which is misleading).
Here is a sample of the Python code I was using...
data = {
'grant_type': 'authorization_code',
'code': request.GET.get('code'),
'redirect_uri': 'http://127.0.0.1:8000/ehr_connection_complete/', # THIS MUST BE AN ABSOLUTE URL
'client_id': '11111111-2222-3333-4444-555555555555',
}
response = post(url, data)
It felt odd to me that an error with the redirect_uri parameter generates an error message about invalid_client, but it's true with Epic's test FHIR server.
I hope this information helps others.
Everything worked fine, until I started to get this error when I use "ask dialog":
Error: This utterance did not resolve to any intent in your skill. Please invoke your skill and try again with a different utterance or update your interaction model to include this utterance before testing again.
After a few tries, I tried to deploy my changes, and see if I can test properly in the console.
It didn't work, although it seems the deployment was successful. I tried to change my profile in "ask init", I tried to remove and reinstall ask-CLI, but this also didn't work.
I tried to clone a skill I had created in the console, and it worked, but when I tried to make changes and deploy, the error came back.
I tried to use "ask simulate" with --force-new-session, but it still didn't work.
I ran "ask dialog" and "ask simulate" with --debug,
This is the output-
{
"id": "fb2869d0-a324-42f6-bca9-4adc0af3476f",
"status": "FAILED",
"result": {
"alexaExecutionInfo": {
"consideredIntents": [
{
"name": "<IntentForDifferentSkill>"
}
]
},
"error": {
"message": "This utterance did not resolve to any intent in your skill. Please invoke your skill and try again with a different utterance or update your interaction model to include this utterance before testing again."
}
And when I inserted it to lambda test, the output was-
{
"errorType": "TypeError",
"errorMessage": "handlerInput.t is not a function",
"trace": [
"TypeError: handlerInput.t is not a function",
" at Object.handle (/var/task/index.js:133:42)",
" at GenericRequestDispatcher.<anonymous> (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:210:59)",
" at step (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:44:23)",
" at Object.next (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:25:53)",
" at fulfilled (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:16:58)"
]
}
The lines in the code-
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
const speakOutput =handlerInput.t('ERROR_MSG');//line 133:error
console.log(`~~~~ Error handled: ${JSON.stringify(error)}`);
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
I don't know why handlerInput.t suddenly doesn't work, however the problem is in errorIntentHandler, so besids this error, we have the original error.
I tried to debug using my own Node.js endpoint (which, unsurprisingly, didn't work), and it seems that there is a problem with the the endpoint connection.
When it worked before, I usually received a request JSON into my commend line, but now the connection fails before that.
Note:
I tried to open a lot of new projects, so I could check this out.
It didn't worked even after creating new project with ask init and immediately ask deploy.
Why does cognito throw ErrCodeNotAuthorizedException "NotAuthorizedException" when the status of the user is already confirmed when making a request to cognito to confirm the user.
The documentation specifies that ErrCodeNotAuthorizedException is thrown when a user is not authorized.
https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.ConfirmSignUp
How should we handle this case? As it would be unclear if we made a request with invalid client secret as it would throw the same error.
Since the code is the same for the unauthorized case and user already confirmed case, the only possible way to differentiate the cases is to match the awsErr.Message() which provides the clear description of the error.
if awsErr, ok := err.(awserr.Error); ok {
switch awsErr.Code() {
case cognitoidentityprovider.ErrCodeNotAuthorizedException:
if awsErr.Message() == "User cannot be confirm. Current status is CONFIRMED" {
log.Println("Handle user already confirmed")
} else {
log.Println("Handle not authorized case")
}
...
default:
}
}
I used YouTube Data API to enable a web-based client I produced for a customer to connect to youtube and list out the videos on her account. This was working fine until the customer left those services unused for 90 days after which the credential was canceled.
I created a new credential but now it doesn't work and I can't figure out the solution. The client authorizes using OAuth then does a YouTube Youtube.Search.list
try{
youtube = getYouTubeService(XXXXXX);
}catch(NullPointerException e){
throw new BasicException("You must connect this system to YouTube before you can load videos. You may do this in Settings.");
}
HashMap<String, String> parameters = new HashMap<String,String>();
parameters.clear();
parameters.put("part", "id,snippet");
parameters.put("forMine", "true");
parameters.put("type", "video");
parameters.put("maxResults", "50");
YouTube.Search.List searchListMineRequest = youtube.search().list(parameters.get("part").toString());
if (parameters.containsKey("maxResults")) {
searchListMineRequest.setMaxResults(Long.parseLong(parameters.get("maxResults").toString()));
}
if (parameters.containsKey("forMine") && parameters.get("forMine") != "") {
boolean forMine = (parameters.get("forMine") == "true") ? true : false;
searchListMineRequest.setForMine(forMine);
}
if (parameters.containsKey("type") && parameters.get("type") != "") {
searchListMineRequest.setType(parameters.get("type").toString());
}
SearchListResponse response = searchListMineRequest.execute();
It is the execute command that produces the problem. The user connects using OAuth in another part of the program and the refresh token is stored for use with the above code. I am new to the google api so excuse if I'm getting some code wrong. I am getting a 403 error. However since the code worked before the credential was cancelled, then I conclude that the problem is with the new credential.
I get the following error message:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "usageLimits",
"message" : "Access Not Configured. YouTube Data API has not been used in project XXXXXXXXXX before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=XXXXXXXXXX then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"reason" : "accessNotConfigured",
"extendedHelp" : "https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=XXXXXXXXXXXXX"
} ],
"message" : "Access Not Configured. YouTube Data API has not been used in project X before or it is disabled. Enable it by visiting.......
}
I found the solution. Once the project credential reached 90 days, my google console project was no longer a functioning project. Some things would work, others not. I had to completely delete the project, connect the youtube data API again and issue a new credential. This resolved the problem.