I have an iOS app that uses an AppSync GraphQL API created with AWS Amplify. I want some queries to be available with public auth (for unauthenticated users) via IAM. For this public case, when calling the API, I get the following error from AWSAppSync/AWSAppSyncClient/AWSAppSyncHTTPNetworkTransport.swift:
Fatal error: Credentials Provider and endpoint not set
The AppSync initialization is done in the AppDelegate:
let serviceConfigIAM = try AWSAppSyncServiceConfig(forKey: "bookapi_AWS_IAM")
let cacheConfigIAM = try AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true, appSyncServiceConfig: serviceConfigIAM)
let clientConfigIAM = try AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfigIAM, cacheConfiguration: cacheConfigIAM)
appSyncClient = try AWSAppSyncClient(appSyncConfig: clientConfigIAM)
I noticed in AWSAppSyncClientConfiguration#makeNetworkTransportForIAM that the resolvedCredentialsProvider is nil:
// Evaluates to nil
let resolvedCredentialsProvider = authProvider ?? AWSServiceInfo().cognitoCredentialsProvider
Do I need to create a credentials provider and pass it to the AppSync client config? How can I use IAM for unauthenticated users?
Related
I have a project setup like this:
React frontend
-> authenticates against...
Identity Server
-> which redirects to...
A Microsoft login
I'm using a Clients Credential Provider and it works great - the IS4 redirects to MS login, and then gets redirected with the access token back, which is then passed on to the React app.
Now, I've been tasked with creating a feature to change the user's password. I'm trying to do this by sending the old+new password to IS4, and then calling the MSGraphClient, but I couldn't make it work.
I've tried the Username/Password provider, because I have all the info needed, but I need to change stuff on the ActiveDirectory settings to make my app public. But even then, I don't like that solution.
I've also tried with the On-behalf-of provider, this is the code:
var scopes = new[] { "User.Read",
"Directory.AccessAsUser.All" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "~~";
// Value from app registration
var clientId = "~~";
var clientSecret = "~~";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// This is the incoming token to exchange using on-behalf-of flow
var oboToken = HttpContext.Request.Headers.First(h => h.Key == "Authorization").Value.ToString().Replace("Bearer ", "");
var cca = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
// DelegateAuthenticationProvider is a simple auth provider implementation
// that allows you to define an async function to retrieve a token
// Alternatively, you can create a class that implements IAuthenticationProvider
// for more complex scenarios
var authProvider = new DelegateAuthenticationProvider(async (request) => {
// Use Microsoft.Identity.Client to retrieve token
var assertion = new UserAssertion(oboToken);
var result = await cca.AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync();
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
var graphClient = new GraphServiceClient(authProvider);
And it kinds of work, because the request is made, but the server throws an error:
AADSTS5002727: Invalid JWT header type specified. Allowed types: 'JWT','http://openid.net/specs/jwt/1.0'.
I checked my token on JWT.io, and the typ is at+jwt... Why? Why is MS sending me a type of token that it doesn't support? How can I change it from my side so it's a plain JWT?
Thanks for any advice, and any other possible solution for this.
To resolve the error "AADSTS5002727: Invalid JWT header type specified. Allowed types: JWT,http ://openid.net/specs/jwt/1.0" , please try the below if helpful:
Please check the version of .Net core you are currently using to generate the token. Try using .Net core 2.2 with IS4.
Try setting IdentityServerOptions.AccessTokenJwtType to empty string or JWT on IdentityServerOptions.
In the mentioned code, replace var oboToken variable directly with the value of token.
var oboToken = "JWT_TOKEN_TO_EXCHANGE";
Please note the below point from MsDoc :
Don't attempt to validate or read tokens for any API you don't own,
including the tokens in this example, in your code. Tokens for Microsoft services can use a special format that will not validate as
a JWT, and may also be encrypted for consumer (Microsoft account)
users
If still the error persists, try upgrading clients to a new token validation library that works with the new style tokens.
Please check whether the below links give you any pointer to resolve the issue:
JWT Token always Invalid · Issue #905 · openiddict/openiddict-core · GitHub
IdentityServer .Net Core 3.0 & Owin/Katana Token validation · Issue #3705 · IdentityServer/IdentityServer4 · GitHub
I have a working ServiceStack API that authenticates against a AzureAD tenant. We are trying to move this to start using Azure B2C. The application is build with c# and runs on net 5.0. I've managed to change the configuration to use the 'correct' config. I'm then using Postman to get my access token from my tenant suing the authorization code flow.
However, when i make a request to the api, the response is always a 401 status code.
Where in the servicestack code can I put a break point to see why this failure is happening? I have tried multiple places in our AppHostConfigurator.cs/AppHost.cs files, but the break points doesn't appear to display why a 401 is being sent back as a response. I'm sure it's something related to wrong claims/roles expected etc, maybe the Azure ADB2C application being setup incorrectly, but obviously i need to know exactly so that i can resolve.
I'm setting up the authentication like this:
private static void ConfigureAuthentication(IAppHost host)
{
var authProviders = new List<IAuthProvider> {new NetCoreIdentityAuthProvider(host.AppSettings)};
if (host.AppSettings.GetAllKeys().Contains("AzureAdB2C"))
{
var debugMode = host.AppSettings.Get(nameof(HostConfig.DebugMode), false);
var azureSettings = host.AppSettings.Get<AzureAdB2COptions>("AzureAdB2C");
var jwt = azureSettings.GetB2CJWTProviderReader(debugMode);
jwt.PopulateSessionFilter = (session, payload, request) =>
{
if (session.Email == null && payload.ContainsKey("upn") && payload["upn"].Contains("#"))
session.Email = payload["upn"];
if (session.UserName == null && payload.ContainsKey("unique_name"))
session.UserName = payload["unique_name"];
};
authProviders.Add(jwt);
}
var auth = new AuthFeature(() => new AuthUserSession(), authProviders.ToArray())
{
HtmlRedirect = "/account/signin",
HtmlLogoutRedirect = "/account/signout",
IncludeAssignRoleServices = false,
IncludeRegistrationService = false
};
// remove default service authentication services
auth.ServiceRoutes.Remove(typeof(AuthenticateService));
host.Plugins.Add(auth);
}
We are using swagger as well to call the API (which works as expected). This question is more about that requests that are submitted with a bearer token.
thanks
Please refer to this existing answer for examples of how to validate why a 3rd Party JWT Token is invalid with ServiceStack's JWT Auth Provider.
API gateway has in-built functionality to perform authorization.
But the examples provided by awslabs have lambda hooked to API gateway, where lambda is authorizing as per this code for a below API gateway:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
DefaultAuthorizer: MyLambdaRequestAuthorizer
Authorizers:
MyLambdaRequestAuthorizer:
FunctionPayloadType: REQUEST
FunctionArn: !GetAtt MyAuthFunction.Arn
So, auth token provided by client is received by lambda and then authorised:
exports.handler = async function (event) {
const token = event.queryStringParameters.auth.toLowerCase()
....
switch (token) {
case 'allow':
return generateAuthResponse('user', 'Allow', methodArn)
case 'deny':
return generateAuthResponse('user', 'Deny', methodArn)
default:
return Promise.reject('Error: Invalid token') // Returns 500 Internal Server Error
}
}
but this is not in-built authentication provided by API gateway.
How does API gateway provide in-built authentication?
Solution 1 :
Create and Use Usage Plans with API Keys : In this method you can use API Keys to authenticate a user. follow this link : API Keys method
Solution 2 :
You can AWS Amazon Cognito service for authentication. It is same as OKTA service. Follow this link : Amazon Cognito
I have successfully created a Cognito user pool and identity pool using aws amplify, and am able to use the documented process to login using the provided authUI. Once logged in, I can retrieve a jwt token via the provider response.... (not complete below so ignore any syntax errors)
AWSAuthUIViewController.presentViewController(
with: self.navigationController!, configuration: nil,
completionHandler: { (provider: AWSSignInProvider, error: Error?) in
if error != nil {
print("Error occurred: \(String(describing: error))")
} else {
// Sign in successful.
print("sign in - token = \(provider.token())")
var tokentask = provider.token()
var output = tokentask.result
}
})
I can then use that token (output) to authentication against an API gateway resource successfully. My problem is I cannot get the token at any other time. I cannot find the correct object to use to try to retrieve that token or cannot find the location where the token is cached so I can reuse it at other times in the app. Any assstance would be appreciated!
i've managed to find out the best way -
AWSCognitoUserPoolsSignInProvider.sharedInstance().getUserPool().token().continueWith { (AWSTask) -> Any? in
if AWSTask.error == nil {
print("Token \(String(describing: AWSTask.result))")
}
return nil
}
This returns the token via awstask.
i am using amazon to store my app picture but when i try to initialize the Amazon Cognito credentials provider i get an exception. here is the initialization
the log is about sharedpref null pointer exception
// Initialize the Amazon Cognito credentials provider
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
this,
"someidentity", // Identity pool ID
Regions.US_EAST_2 // Region
);
the problem was that when i intialized the:
AmazonS3 s3 = new AmazonS3Client(credentialsProvider);
i did it with "credentialsProvider" function whice is what i posted above
to solve this i just called the static credentialProvider that amazon provide "sCredProvider" in the Util Class
so the function looks like this:
AmazonS3 s3 = new AmazonS3Client(sCredProvider);
which inherit from the "UtilAWS3" Class
hope this helps anyone who gets this weird problem :)