I am setting up a microservice application where each microservice is an independent loopback application, and one of this microservice is the gateway which holds user accounts and accessToken models. all the other microservice should query the gateway if they need to validate an AccessToken.
if authentication is enabled I understand loopback does an AccessToken.findByid to initialize the AccessContext with the token information so we can authorize the requests.
In my case the loopback app does not own the AccessToken model and as expected it fails with following error:
{ "error": {
"statusCode": 500,
"name": "Error",
"message": "Cannot call AccessToken.findById(). The findById method has not been setup. The PersistedModel has not been correctly attached to a DataSource!",
}
My question is: what is the best way to override the AccessToken lookup so that it queries the gateway microservice instead?
In each microservices you need atach your AccessToken to gateway db. Try something like this:
app.loopback.AccessToken.attachTo(app.datasources.db); // db <-- gateway dataSource
app.model(app.loopback.AccessToken);
Related
I have an express application running on an ECS instance, which is connected to the API Gateway. Users are authenticated by Cognito. I added the Cognito authorizer in front of the endpoints, and it works just fine. My problem is that I don't want to decode the token in the backend, and I want Cognito authorizer to pass the verified and already decoded claims into the request.
I am aware that from the integration request, using mapping templates, I can map the values and append them to the body of the request using following.
{
"context": {
"email": "$context.authorizer.claims.email"
}
}
But I am using http proxy, and mapping template is not available when using gateway as proxy.
I was wondering if there is way to make this possible while using the api gateway as http proxy.
It seems that you can also append into params, query, or header by mapping a name to the value: context.authorizer.claims.email
Assuming I mapped it into header, within the app I can get this as req.headers.{name}
I would like to use firebase auth to secure my AWS AppSync graphql api, as opposed to something like Cognito. There are several reasons behind this like pricing and us already using some other firebase services.
The only viable solution I can see atm is to somehow pass firebase user token to my AppSync graphql api and validate it through OpenID Conneect / OIDC.
I was not able to figure this out nor find any guides on the topic, so wanted to ask here if it is even possible, if so, could any examples be provided or perhaps use-full references?
Here are related fields that need to be provided to AppSync in terms of OpenID connect data https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-appsync.OpenIdConnectConfig.html
Do these exist for firebase auth?
Update: I was able to find some documentation on firebase token verification https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library
I believe following URL is what needs to be used as OpenID url setting in AppSync https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com
However AppSync documentation states that they append /.well-known/openid-configuration to such url and when I do that to googleapis url above, it throws 404.
AppSync expects an OpenID Connect Provider (OP). More specifically, it wants the OP's issuer; the rest of metadata is retrieved based on that. From AppSync doc:
OPENID_CONNECT Authorization
This authorization type enforces OpenID Connect (OIDC) tokens provided by an OIDC-compliant service.
Your application can leverage users and privileges defined by your
OIDC provider for controlling access.
An Issuer URL is the only required configuration value that you
provide to AWS AppSync (for example, https://auth.example.com). This
URL must be addressable over HTTPS. AWS AppSync appends
/.well-known/openid-configuration to the issuer URL and locates the
OpenID configuration at
https://auth.example.com/.well-known/openid-configuration per the
OpenID Connect Discovery specification
Firebase is (mostly) a middleware. Even though you could have a user account be residing in Firebase, a more typical use case is to plug in some provider such as Google or Microsoft into Firebase. You could then use Firebase APIs to perform various operations without having to know the details of the underlying provider.
Whether acting as middleware or as an identity store with users in Firebase, it's unclear if Firebase is an OIDC-compliant provider. OpenID publishes OIDC conformance tests as well as entities that have been certified. The only Google entity on the latter list is a "Google Federated Identity". Certification aside, Firebase does issue a signed JWT that according to them is morally equivalent to id_token in OIDC:
ID token verification
If your Firebase client app communicates with
your backend server, you might need to identify the currently
signed-in user on your server so you can perform server-side logic on
their behalf. You can do this securely by using ID tokens, which are
created by Firebase when a user signs into an Firebase app.
ID tokens
conform to the OpenID Connect spec and contain data to identify a
user, as well as some other profile and authentication related
information. You can send, verify, and inspect these tokens from your
own backends. This allows you to securely identify the currently
signed in user and authorize them into your own backend resources.
If you create a Firebase project, then authenticate via that and examine the issued token, you'll see the iss (Issuer) key in the token payload. It has a value of https://securetoken.google.com/<Firebase projectId> and that's the URL needed by AppSync.
You can confirm that OIDC metadata is available by concatenating /.well-known/openid-configuration with https://securetoken.google.com/<Firebase projectId> and doing a GET on the resulting URL. The expected response should look like this:
{
"issuer": "https://securetoken.google.com/<Firebase project id>",
"jwks_uri": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken#system.gserviceaccount.com",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
]
}
I'm using aws sdk to manage logins and signups on my app and the sdk requires my app client to have no secret key. This is a problem since I intend to use Cognito to authenticate the API gateway. I tried to enable OAuth 2.0 client credentials flow on app client settings, select a scope and give it to the API method, but when I try to save the app client settings I get:
"We were unable to update your App Configuration: client_credentials flow can not be selected if client does not have a client secret. (Service: AWSCognitoIdentityProviderService; Status Code: 400; Error Code: InvalidOAuthFlowException"
Is there a better way to configure API Gateway with Cognito? Am I doing something wrong?
You need to create a new app client for your API with a secret.
front end client - app client with out secret
Backend client / api - app client with secret
Client credentials is a usually a method for service-service authentication. You have to store the secret securely.therefore insecure to use in an unsafe front end app such as an html website.
Hope this helps.
To add to Arun's answer, the API should not need a Cognito OAuth client entry and just needs to validate received Cognito access tokens.
Typically this is done by downloading JWKS keys from Cognito - and API Gateway has a setting that automatically does this.
I can provide further details if you run into problems identifying the user after token validation.
I have a series of microservices on GCP Cloud Run that are each hosting a federated Apollo GraphQL service. I then have one last container which is acting as a federated GraphQL gateway for the rest of the services.
This works fine when public access is enabled on the containers but I cannot get the gateway server to authenticate against the other two services.
I have tried using the Apollo RemoteGraphQLDataSource and implementing the willSendRequest method to set the neccessary headers.
I have also tried adding the cloud run invoker role to the service role that the gateway runs as.
const servicex = new RemoteGraphQLDataSource({
url: serviceurl,
willSendRequest({ request, context }) {
request.http.headers.set(
"Authorization",
"Bearer ${TOKEN}"
);
}
});
const gateway = new ApolloGateway({
serviceList: [
servicex
]
});
const { schema, executor } = await gateway.load();
const server = new ApolloServer({ schema, executor })
I expect the gateway server to be able to authenticate against the other microservices.
Cloud Run authorization requires an OAuth 2.0 Identity Token in your authorization: bearer TOKEN HTTP header. A common mistake is to use an Access Token.
Unless you have specified a new service account in your deploy command, Cloud Run uses the Compute Engine Default Service Account as its identity. This means you need to specify the Service Account email address for the role/run.Invoker.
When you authorize with OAuth for User Credentials, you can receive three tokens: Access Token, Refresh Token and Identity Token depending on what you have specified in the Scopes parameter. The Identity Token is what you use in your HTTP Authorization header.
When you authorize service-to-service using a service account identity, call the Cloud Run metadata server to create a token for you. This endpoint will return an Identity Token. Use the returned token in your HTTP Authorization header.
http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=<ususally-the-url-of-the-cloud-run-service-you-are-calling>
The returned token is a Signed JWT that you can decode. Typical base64 encoding of header.payload.signature. The payload contains the email address of the service account and this email address is what the Cloud Run Proxy uses for authorization.
I'm writing a web app which is using AWS Cognito UserPools for user authentication and IdentityPools for granting direct access to an S3 bucket.
This JavaScript web app has its own App client ID in the UserPool with which it interacts with it.
I also have a couple of Lambda functions doing some admin functions towards the UserPool and IdentityPool. These lambda functions have their own App client ID.
I used to have only a single Authentication provider added to the IdentityPool, with the same App Client ID that the lambda functions have set.
In this setup, the web app was having issues. I was able to authenticate towards the UserPool, but when the identity token was sent to the IdentityPool, I received an error "Token is not from a supported provider of this identity pool.". I believe this was because the aud parameter of the JWT was set to the UserPool ID which was not added to the IdentityPool
I eventually realized that I could get rid of the error if I added another "Authentication provider" to the IdentityPool, filling in the same UserPool ID and the other App client ID.
My question now is - is this the correct approach? Am I perhaps misunderstanding the App client ID meaning and usage? Am I way off base with my approach?
Yes that sounds like a perfectly good approach. If you don't encounter any security or functional issues it is the "correct" approach.