how to handle the access token in loopback - loopbackjs

This is the boot script code I have added
module.exports = function enableAuthentication(server) {
//enable authentication
server.enableAuth();
};
In postman tool i have tried by setting authorization in header as below for users/logout api (note: I created the user model by extending User model),
authorization LqAHkjJV4JQ7oiW6QrYPeDoJszqUXSSUi7NwTHivKV0jyNK3VSyIyFxon72NfPzZ
But iam getting the below error,
{"error":{"name":"Error","status":500,"message":"could not find accessToken","stack":"Error: could not find accessToken\n at D:\\zauth\\node_modules\\loopback\\common\\models\\user.js:302:12\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:2056:62\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:1984:11\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:396:17\n at async.each (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:153:20)\n at _asyncMap (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:390:13)\n at Object.map (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:361:23)\n at allCb (D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:1912:15)\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\connectors\\memory.js:472:7\n at _combinedTickCallback (internal/process/next_tick.js:67:7)\n at process._tickDomainCallback (internal/process/next_tick.js:122:9)"}}
Also i have tried,
http://localhost:3000/api/users/logout?access_token=LqAHkjJV4JQ7oiW6QrYPeDoJszqUXSSUi7NwTHivKV0jyNK3VSyIyFxon72NfPzZ
It's not woking, same error
i have to know how to pass the accesstoken #kamal0808

You need to set ACLs(Access Control Levels) for every API request for the application.
Here's the doc link for ACLs:
https://loopback.io/doc/en/lb2/Controlling-data-access.html
For your code, you need to get the following object in ACLs array of your Users.json file:
{
"accessType": "EXECUTE"
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW",
"property": "find"
}
$authenticated refers to anybody who can log in. You can create custom roles for users too.

The default ACL for built-in User model does not allow listing users:
You can see full configuration here:
https://loopback.io/doc/en/lb3/Managing-users.html#default-access-controls
The above ACL denies all operations to everyone, then selectively allows:
Anyone to create a new user (User instance).
Anyone to log in, log out, confirm their identity, and reset their own password.
A user to perform deleteById, findById, and updateAttributes on their own User record (instance).
You can not directly modify build-in User model, so you will need to extend it.
How to pass access token after successful login?
You have two ways to do that:
As access_token query parameter (Not recommended). Example:
http://localhost:3000/api/users/me/orders?access_token=$ACCESS_TOKEN
As "Authorization: HTTP header (Recommended). Example:
Authorization: $ACCESS_TOKEN
You should use HTTP headers, because using query parameters is not secured enough (they are not crypted by SSL protocol and could stay in browser or other client history).
Check for more information the documentation: https://loopback.io/doc/en/lb3/Making-authenticated-requests.html

Related

Using Postman to access Google Indexing APIs

I would like to test Google Indexing API with Postman.
I have already configured the api correctly on https://console.cloud.google.com/ for using it on a wordpress site with the plugin Instant indexing and everything works ok.
Now I simply want to do a POST call with Postman, in particular I did not found anything clear about the type of authentication (Oauth 2) to use for the call.
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "google.indexing.v3.UrlService.PublishUrlNotification",
"service": "indexing.googleapis.com"
}
}
]
}
}
Anyone can provide me an example? Thank you.
As stated in the comments, Google API's normally use OAuth2. Some exceptions might use an API key, but even then most of those still limit the data you can access with an API key versus what you can access using OAuth2
This particular API does indeed use OAuth2 as per the docs. This means you need an access token (as the error message states), so you will need to generate one.
Easiest would be to use a library for one of the many programming languages, as per the docs here.
I'll give a simple example in python as that is my preferred language. The following code uses the google-auth library (docs). It's the base library to deal with credential objects. You will need to download the JSON representation for the service account you created. See the relevant docs. Note that you should handle this file as if it were a password, same as with the access token itself, although the access token has a default lifetime of 3600 seconds, AKA 1h.
If you get an error message that the token is expired, create a new one. Read up on refresh tokens for that, but that's beyond the scope of this question.
Code was tested on a virtualenv for python 3.10
requirements.txt
google-auth
requests
main.py
from google.oauth2 import service_account
import google.auth.transport.requests
# create credentials object
creds= service_account.Credentials.from_service_account_file("/path/to/serviceaccount.json")
# we need a scoped credentials object as per
# https://developers.google.com/search/apis/indexing-api/v3/prereqs#requirements
scoped_creds = creds.with_scopes(['https://www.googleapis.com/auth/indexing'])
# create a request object for the refresh method
request = google.auth.transport.requests.Request()
# make the library do it's magic and get a token
scoped_creds.refresh(request)
print(scoped_creds.token)
This will print out an access token. (if it prints with dots (....) at the end, remove them.
Another easy option would be to (ab)use the gcloud command line tool. There's a couple of steps to this to get to work. You can use the cloud shell as an easy way to do this.
Download or copy the serviceaccount JSON file I mentioned above.
Activate the serviceaccount in gcloud using:
gcloud auth activate-service-account SERVICE_ACCOUNT#DOMAIN.COM --key-file=/path/key.json
Print the access token with this command:
gcloud auth print-access-token
The hard and masochistic way would be to use something like CURL or HTTPS requests manually to generate the token. Feel free to do so, but I'm just going to point you to the docs for that. It's a bit of a pain in the posterior.
You can test the token as explained in this answer.
Now that you have the access token, you can use it in POSTMAN by setting it in the header for the call. See this nice answer, but basically add the following request header key/value pair, replacing TOKEN with the generated token.
KEY: Authorization
VALUE: Bearer TOKEN
For anyone interested I managed to make it work by following these steps:
To obtain the authentication token with Postman first go to https://console.cloud.google.com/apis/credentials and in your web application ID client set https://oauth.pstmn.io/v1/callback as Authorized redirect URIs
Now in Postman Application in Authorization tab select OAuth 2.0 and configure the fields according to your client_secret.json (image)
Now click on 'Get new access Token' and you should get it
Specify the url and type as raw json body (image)

AWS Cognito - Create a user via API Endpoint in Postman

Does anybody know if I can make a request to create or a sign up a user in AWS Cognito user pool?
For example, something like below is to display the login screen.
But is there a POST request or endpoint I can call to create a user?
I tried looking through their documentation but no look finding anything concrete.
Keep in mind, if it possible I would like to populate a value for a custom attribute I created.
This is the main reason why I am looking for an endpoint because I cannot seem to find a way to populate the value for a custom attribute via the AWS interface.
So technically I do not need an endpoint if it is possible to populate a custom attribute per user in AWS.
GET https://mydomain.auth.us-east-1.amazoncognito.com/login?
response_type=code&
client_id=ad398u21ijw3s9w3939&
redirect_uri=https://YOUR_APP/redirect_uri&
state=STATE&
scope=openid+profile+aws.cognito.signin.user.admin
It looks like what you need is https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html or https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SignUp.html. As far as I'm aware, there is no way to prepopulate the attribute on the Cognito hosted UI. You did not specify what programming language you are using, but at the bottom of the page there are links to documentation with examples for different SDKs. The difference between these two approaches is discussed here: https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html. So in this case, AdminCreateUser corresponds to option 3 and SignUp to option 1. The difference is mainly in whether or not the user will receive an invite. Also, for AdminCreateUser Cognito will generate a temporary password and require user to enter a new password the first time they log in.
Body
{
"ClientId": "test",
"Password": "Qwerty123",
"UserAttributes": [
{
"Name": "email",
"Value": "test#test.com"
}
],
"Username": "test#test.com"
}
Headers
URL
POST https://cognito-idp.eu-west-1.amazonaws.com/ HTTP/1.1

Switching unauthenticated user to authenticated user

I'm trying to set my app up to allow unauthenticated users to access an AppSync API, as mentioned in https://docs.aws.amazon.com/cognito/latest/developerguide/switching-identities.html . Ideally they would be able to start using the app, then sign in and keep all their data.
I've got:
A user pool. This is set up for Google auth/regular cognito auth
An identity pool
This is linked to the user pool via a Cognito identity provider.
The authenticated/unauthenticated roles have a policy attached to them that gives them access to the GraphQL API
An AppSync API set up with AWS_IAM auth
I create the app sync client like this:
val credentialsProvider = CognitoCachingCredentialsProvider(
context,
"us-east-2:abc...etc",
Regions.US_EAST_2)
appSyncClient = AWSAppSyncClient.builder()
.context(applicationContext)
.awsConfiguration(awsConfiguration)
.credentialsProvider(credentialsProvider)
.build()
This works fine and the identity pool creates an identity for me, and I can interact with the API. Well, it creates two anonymous identity IDs, but it works. The real trouble comes when I log in:
val hostedUIOptions: HostedUIOptions = HostedUIOptions.builder()
.scopes("openid", "email", "aws.cognito.signin.user.admin")
.build()
val signInUIOptions: SignInUIOptions = SignInUIOptions.builder()
.hostedUIOptions(hostedUIOptions)
.build()
runOnUiThread {
mobileClient.showSignIn(
mainActivity,
signInUIOptions,
object : Callback<UserStateDetails?> {
override fun onResult(result: UserStateDetails?) {
Log.i("AwsAuthSignIn", "onResult: " + result?.userState)
}
override fun onError(e: Exception?) {
Log.i("AwsAuthSignIn", "onResult: " + result?.userState)
}
}
)
}
After that I see that it's created a new identity associated with the sign in, rather than use the old one. I thought it was supposed to seamlessly transfer over the old identity ID to be connected with the authenticated user.
I've also tried calling registerIdentityChangedListener to see if it fires on logging in, but it does not. It only fires when first getting the unauth identity IDs.
Also when I log into the same account from two different device it creates two different identity IDs for the same user in the user pool. Since I'm using identityId to track RDB record ownership, this means that the same user sees different items after logging in.
So is identityId the right thing to put in the database? Is it expected to be different for different devices? I'm trying to find something else to use but am coming up dry.
This is what's available in the "identity" section of the context for use with VTL resolvers:
"identity": {
"accountId": "________",
"cognitoIdentityAuthProvider": "\"cognito-idp.us-east-2.amazonaws.com/us-east-2_______\",\"cognito-idp.us-east-2.amazonaws.com/us-east-2_______:CognitoSignIn:____________\"",
"cognitoIdentityAuthType": "authenticated",
"cognitoIdentityId": "us-east-2:___",
"cognitoIdentityPoolId": "us-east-2:___",
"sourceIp": [
"_____"
],
"userArn": "arn:aws:sts::_________:assumed-role/amplify-focaltodokotlin-prod-222302-authRole/CognitoIdentityCredentials",
"username": "__________:CognitoIdentityCredentials"
}
"username" is the only other one that makes sense, but when I call AWSMobileClient.username on my side, it comes up with a different format: "Google_". So I wouldn't be able to match it up in client-side logic.
Is this possible at all or do I need to abandon the idea of unauthenticated use and go with User Pools directly?
I'll have a shot of answering this, I'll stick to rather how Cognito works than what to do with a specific SDK.
Quick recap, in Cognito, when a user authenticates, they will get 3 tokens, access, id & refresh. With an Identity Pool, they can exchange one of these (I forget which one) to get short-term credentials to assume a role. STS is what is used under the hood for this, and that's what you see in the userArn there. You don't want to look at that guy for an ID, it's an STS construct your client needs to assume an IAM role.
I'll go back to the tokens, lets look at the id_token, my favourite:
{
"at_hash": "l.......",
"sub": ".....",
"cognito:groups": [
"ap-southeast-......_Google",
"kibana"
],
"email_verified": false,
"cognito:preferred_role": "arn:aws:iam::...:role/...",
"iss": "https://cognito-idp.ap-southeast-2.amazonaws.com/ap-southeast-...",
"phone_number_verified": false,
"custom:yourapp:foo": "Bar",
"cognito:username": "Google_......",
"nonce": ".........",
"cognito:roles": [
"arn:aws:iam::.....",
"arn:aws:iam::....."
],
"aud": ".....",
"identities": [
{
"userId": "....",
"providerName": "Google",
"providerType": "Google",
"issuer": null,
"primary": "true",
"dateCreated": "1583907536164"
}
],
"token_use": "id",
"auth_time": 1596366937,
"exp": 1596370537,
"iat": 1596366937,
"email": "test#test.com"
}
I have a Google one here too, I removed a bit of stuff to hide my account etc, but anyway the id you want to use is cognito:username which will in-fact be in the form of Google_. This is internal and typically you would not show this to users. So instead in Cognito you can use the another claim preferred_username, which can also be used as an alias to sign-in as mentioned here but not for external identity providers.
You can use create custom claims to help show information on the UI, which will be prefixed with custom:, I have one here: custom:yourapp:foo. But there might be one existing for you already such as email which is available from Google. When you created your external identity provider you would have configured what claims you wanted to map from Google, email would have been there, so in your app you can read the email claim, but you should use the cognito:username in your App's backend, but keep in mind that if a user deletes and recreates their account I don't know that you get the same ID again. You may rather want users to be able to define a preferred_username on signup, which you could display in the UI, but don't use that to save data against, use the cognito:username claim.
And now for Start using the app, then sign in and keep all their data. Typically this would be implemented by saving all the data in local storage on the device, not the backend. The reason being is that if a user has not authenticated, (excluding creating a session upon opening the app), then there is no way to verify that foo#gmail.com is actually foo#gmail.com when they were hitting your API as what you saw with the unauthenticated role. I could hit your API and say that I am foo#gmail even though I was bar#gmail.
The neatest way would be to store the data locally on the device, so it wouldn't matter if the user was authenticated or not. If for some reason your app does need to store this data in the backend to function, and you cannot refactor, what you could do is use a Customized Userpool Workflow, and create a Pre-Signup Lambda, which could take a custom claim (I wouldn't use the sts userArn seems wrong but you could use that too), preferably a GUID of a shopping cart for example custom:yourapp:cartGuid. So what I mean is:
When an anonymous user visits the app for the first time, they would
be issued a GUID for a shopping cart, and save all items in that
cart.
If they choose to sign up, they can pass in a custom claim: custom:yourapp:cartGuid, and in your Lambda function you will create the user in your DB, and add the cart to their account.
Guessing another user's GUID would be near impossible, but if this is a security concern then you could create a signed token.
You probably want to clean up users's carts that don't move to the sign-up after a certain amount of time.
Just give me a comment if you have any questions or are unsure. I believe from memory that you need to use the pre-signup hook because the post-confirm doesn't have access to the claims passed in on the signup process. You may want to create the user with an unconfirmed flag in the pre-hook, and then enable them in the post-hook which I believe is more safe incase another failure would happen your pool and then you have users created in a dirty state. Best of luck I've been through Cognito battles myself and survived!

Custom attribute not passed into ID_TOKEN created by AWS Cognito

I am not able to get custom attribute in ID_TOKEN returned from AWS Cognito after successful user login.
Steps I tried :
1.Created user pool
2.Created app client and checked the custom attribute(customattrib1,customattrib2)
User Pool screen :
Check custom attribute in app client config
3.Created user using admin-create-user api
Below image shows the value for user attributes:
4.Signed in user using aws-cognito-auth.js in client app.The ID token returned do not contain the custom attribute.
ID_TOKEN
{
"at_hash": "PKfjYDaiEty5mUOyJZlPQA",
"sub": "639d5016-2bd3-4c6f-b82d-21ae38071b09",
"email_verified": true,
"iss": "https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_XXXXXXX",
"phone_number_verified": true,
"cognito:username": "testuser",
"aud": "XYXYXYXYX",
"token_use": "id",
"auth_time": 1549349674,
"phone_number": "##########",
"exp": 1549353274,
"iat": 1549349674,
"email": "testuser#somedomain.com"
}
I have already checked links below, which had some info regarding this issue, but nothing helped so far.
Adding Cognito custom attributes post pool creation?
Cognito User Pool custom attributes do not show up in the ID token if user pool is configured with a SAML identity provider
Cognito User Pool custom attributes do not show up in the ID token if user pool is configured with a SAML identity provider
https://www.reddit.com/r/aws/comments/a07dwg/cognito_add_custom_attribute_to_jwt_token/
Please help me figure out if I am missing something..
In your Cognito user pool go to General Settings -> App Clients, then for each app client click on Show Details, then Set attribute read and write permissions. Check the checkbox next to your attribute name under Readable Attributes.
In your Cognito user pool go to App client settings -> Allowed OAuth Scopes and enable profile scope.
I had the same trouble and your question came up when I was searching for a solution.
My custom attributes started to appear in ID token when I enabled profile scope in 'App client settings'. (available at: AWS console-> 'User pools'-> click your pool -> 'App client settings' -> 'Allowed OAuth Scopes')
(BTW: I was misled by this sentence from the documentation: "The openid scope returns all user attributes in the ID token that are readable by the client". In my case openid scope was not enough.)
For me the problem was that I was getting my token (after authenticating with the Amplify js library) from:
Auth.currentSession().then(u => u.getAccessToken().getJwtToken())
Instead of:
Auth.currentSession().then(u => u.getIdToken().getJwtToken());
After changing it worked fine! Hope it helps!
For anyone coming here that is using the Amplify SDK like the OP - Copy and Paste calls out an important point that your client ALSO needs to explicitly ask for the scope.
In my case we I am using Angular. After adding "profile" to the User Pool "Allowed OAuth Scopes" - you also need to specify it in your client configuration:

How to block Facebook webhook calls for app specific users? [duplicate]

There is documentation for test users in the Facebook Developer online documentation but how do you delete actual users where the application doesn't show in their app list anymore? This is with the knowledge of the access_token and facebook_user_id.
Used to delete Test Users:
https://graph.facebook.com/893450345999?method=delete&access_token=A2ADI1YMySweBABBGrWPNwKMlubZA5ZCrQbxwhtlEd9FIQUrOVjsGD3mnIWEbUhzDz7dkuBekMFdHvjvJ9CZAU7EMSSaZBsgN60FkMCi3AAZDZD
Running the test user link produces the following error:
"error": {
"message": "(#100) Can only call this method on valid test users for your app",
"type": "OAuthException",
"code": 100
}
You seek for application de-authorization:
You can de-authorize an application or revoke a specific extended permissions on behalf of a user by issuing an HTTP DELETE request to PROFILE_ID/permissions with a user access_token for that app.
permission - The permission you wish to revoke. If you don't specify a permission then this will de-authorize the application completely.
To achieve this issue request to:
https://graph.facebook.com/me/permissions?method=delete&access_token=...
Once application de-authorized it will not appear in the list of user's applications.
Update December 2021
Follow the reference for Requesting & Revoking Permissions:
To remove single permission issue a DELETE request to /{user-id}/permissions/{permission-name} passing user access token or an app access token
To de-authorize an app completely issue similar request to the /{user-id}/permissions endpoint
Real users 'delete' themselves from your app when they remove your app from their account, you don't have to do anything.
If you would like to know when users de-authorize your app like this, you can specify a Deauthorize Callback URL in your app's settings. As described in the docs at https://developers.facebook.com/docs/authentication/:
Upon app removal we will send an HTTP POST request containing a single parameter, signed_request, which, once decoded, will yield a JSON object containing the user_id of the user who just deauthorized your app. You will not receive an user access token in this request and all existing user access tokens that were previously issued on behalf of that user will become invalid.
UPDATE: To remove your own app from the user's authorized applications, issue an HTTP DELETE to https://graph.facebook.com/[userid]/permissions?access_token=... as per https://developers.facebook.com/docs/reference/api/user/.
Typically Graph API calls also support doing an HTTP POST with an extra parameter, method=DELETE, in case DELETE calls are not possible/supported.
To do it:
You must have the user access token.
Visit https://developers.facebook.com/tools/debug/accesstoken/ and debug the user access token.
Copy App-Scoped User ID
Via API call HTTP DELETE to https://graph.facebook.com/[App-Scoped User ID]/permissions?method=delete&access_token=[YOUR-APP-ACCESS-TOKEN]