AWS Cognito Auth token - swift - 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.

Related

How to log and respond with error messages from failed JWT authorisation in Ktor?

When I am authorising a request, if any of the standard claims in the JWT are invalid, or if it fails for some other reason (such as the signature being incorrect), I would like to be able to see what exactly was incorrect, especially when testing. Currently, I am not able to see any message in the Unauthorized 401 response, nor in my logs.
My authentication setup (in my Application.module() function), using the auth0-jwt library.
val jwtVerifier = JWT.require(Algorithm.RSA256(getPublicKeyFromString(publicKey), null))
.withAudience("audience")
.acceptLeeway(1)
.acceptExpiresAt(5)
.build()
install(Authentication) {
jwt {
verifier(jwtVerifier)
validate { credential: JWTCredential ->
JWTPrincipal(credential.payload)
}
}
}
#OptIn(KtorExperimentalLocationsAPI::class)
install(Locations) // see http://ktor.io/features/locations.html
install(Routing) {
authenticate {
ServiceEndpoints()
}
}
I have set up an endpoint handler as follows:
fun Route.ServiceEndpoints() {
get<Paths.getData> { params ->
checkCustomClaim(context.authentication.principal(), <some other parameters here>)
//handling code here
}
}
I'll point out that checkCustomClaim() will raise an AuthorisationException (just a simple exception that I created) if the custom claim fails. I do it this way because each endpoint will be checking different information in my custom claims.
I have attempted to get logs and more information in the response with a custom status page. I am able to get the log message and response data for my AuthorisationExceptions, but not for failures in the standard claims.
install(StatusPages) {
exception<JWTVerificationException> { cause ->
log.warn("Unauthorized: ${cause.message}")
this.call.respond(
status = HttpStatusCode.Unauthorized,
message = cause.message ?: "Unauthorized"
)
}
exception<AuthorisationException> { cause ->
log.warn("Unauthorized: ${cause.message}")
this.call.respond(
status = HttpStatusCode.Unauthorized,
message = cause.message ?: "Unauthorized"
)
}
}
You can use information from a JWT diagnostics log that is written on the TRACE level.

google oauth and refresh token confusion/questions

I had expected the refresh of an expired access token to happen during the authentication process instead of during an api access.
I think I understand why this happens - authorization is done once but an access token can expire at any time, therefore a refresh attempt needs to be attempted whenever the token is determined to be expired.
I'd like to confirm this is the right interpretation of what's going on.
My first clue was the part of the docs that said
If you use a Google API Client Library, the client object refreshes
the access token as needed as long as you configure that object for
offline access.
I am using the following:
google-oauth-client 1.24.1
google-oauth-client-java6 1.24.1
google-oauth-client-jetty 1.24.1
When I run with a completely invalid access token ("i am no good") and a valid refresh token and execute a
DCM API call to a com.google.api.client.googleapis.services.json.AbstractGoogleJsonClient subclass, I observe the following behavior:
control passes to com.google.api.client.auth.oauth2.Credential at method:
public final boolean refreshToken() throws IOException {
lock.lock();
try {
try {
TokenResponse tokenResponse = executeRefreshToken();
if (tokenResponse != null) {
setFromTokenResponse(tokenResponse);
for (CredentialRefreshListener refreshListener : refreshListeners)
{
refreshListener.onTokenResponse(this, tokenResponse);
}
return true;
}
} catch (TokenResponseException e) {
boolean statusCode4xx = 400 <= e.getStatusCode() && e.getStatusCode() < 500;
// check if it is a normal error response
if (e.getDetails() != null && statusCode4xx) {
// We were unable to get a new access token (e.g. it may have been revoked), we must now
// indicate that our current token is invalid.
setAccessToken(null);
setExpiresInSeconds(null);
}
for (CredentialRefreshListener refreshListener : refreshListeners) {
refreshListener.onTokenErrorResponse(this, e.getDetails());
}
if (statusCode4xx) {
throw e;
}
}
return false;
} finally {
lock.unlock();
}
}
This goes out and gets a new access token as long as the refresh token is valid (i've tried using an invalid refresh token and watched it fail).
Upon successful retrieval of a new access token, control passes to
refreshListener.onTokenErrorResponse(this, e.getDetails());
The token is inserted into the proper objects and access continues.
If I run with a bad refresh token the above method fails with:
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad
Request
{
"error" : "invalid_grant",
"error_description" : "Bad Request"
}
Can anyone confirm I've got the right general idea?

Unable to Register IOS device to Mobile First 8

I am able to see the iOS device under devices tab in MF, registered to my application. but pushing a notification fails with the below error:
An error occurred while the notification was sent. Internal server error. No devices found.
Upon reviewing IOS code, I noticed the below issue while invoking MFPPush.sharedInstance.registerDevice(nil)
Cannot retrieve a valid authorization header for header. Check resource and authorization server configuration.
I am using the code from the git sample. Below is the snippet throwing the error:
#IBAction func registerDevice(_ sender: AnyObject) {
print("Attempting Device registration with Mobile First")
WLAuthorizationManager.sharedInstance().obtainAccessToken(forScope: "push.mobileclient") { (token, error) -> Void in
if (error != nil) {
print("Did not recieve an access token from server: " + error.debugDescription)
} else {
WLClient.sharedInstance()?.setDeviceDisplayName("White Ipad", withCompletionHandler: { (error) in
if error == nil{
print("device display name is set")
}else{
print("error setting device name: " + error.debugDescription)
}
})
print("Recieved the following access token value: " + (token?.value ?? "no token"))
MFPPush.sharedInstance().registerDevice(nil) { (response, error) -> Void in
if error == nil {
self.enableButtons()
self.showAlert("Registered successfully with Mobile First")
print(response?.description ?? "")
} else {
self.showAlert("Registration failed with Mobile First. Error \(error?.localizedDescription)")
print(error?.localizedDescription ?? "")
}
}
}
}
}
Mobile First Config: I have followed the documentation and configured the UserLogin security check from the sample git project and have removed scope to push.mobileclient under security.
Reading the OAuth Security in MF, i understand the that token is necessary to access resources, but I am unable to figure out how to attach the token in registerDevice().
It seems to be you haven't configured Push Notifications properly in MobileFirst Server.
Make sure that you have added push.mobileclient scope in Security tab of your application. If you are not using any security check, you can add scope like below.
Check whether your application is configured valid iOS provisioning profile enabled with Push Capability
Make sure that you have uploaded valid sandbox/production certificates in Push tab of your particular app in MFP Operations Console.
More details : here
Make sure your app is enabled Push Capability in the project setting and also check you sending device token to MF Server using MFPPush.sharedInstance().sendDeviceToken(deviceToken) API in didRegisterForRemoteNotificationsWithDeviceToken method of AppDelegate file
-

How to Create login and logout using Vapor (Basic Authentication)

I want to create login and logout methods and routes. I've done already basic authentication but now I'm stuck how to continue. How should I do that, should I use sessions?
I'm using Vapor 3, Swift 4 and PostgreSQL and followed this tutorial https://medium.com/rocket-fuel/basic-authentication-with-vapor-3-c074376256c3. I'm total newbie so I appreciate a lot if you can help me!
my User model
struct User : Content, PostgreSQLModel, Parameters {
var id : Int?
private(set) var email: String
private(set) var password: String
}
extension User: BasicAuthenticatable {
static let usernameKey: WritableKeyPath<User, String> = \.email
static let passwordKey: WritableKeyPath<User, String> = \.password
}
UserController.swift, registering user.
private extension UserController {
func registerUser(_ request: Request, newUser: User) throws -> Future<HTTPResponseStatus> {
return try User.query(on: request).filter(\.email == newUser.email).first().flatMap { existingUser in
guard existingUser == nil else {
throw Abort(.badRequest, reason: "a user with this email already exists" , identifier: nil)
}
let digest = try request.make(BCryptDigest.self)
let hashedPassword = try digest.hash(newUser.password)
let persistedUser = User(id: nil, email: newUser.email, password: hashedPassword)
return persistedUser.save(on: request).transform(to: .created)
}
}
}
So in Basic authentication there is no 'logout' per se as there's no login. With HTTP Basic Auth you transmit the user's credentials with each request and validate those credentials with each request.
You mention sessions, but first it's important to know what type of service you are providing? Are you providing an API or a website? They are different use cases and have different (usually) methods for authentication and login.
For an API you can use Basic Authentication and generally in your login function you exchange the credentials for some sort of token. Clients then provide that token with future requests to authenticate the user. To log out you simply destroy the token in the backend so it is no longer valid.
For a website, things are a little different since you can't manipulate the requests like you can with a normal client (such as setting the Authorization header in the request). HTTP Basic authentication is possible in a website, though rarely used these days. What traditionally happens is you submit the user's credentials through a web form, authenticate them and then save the authenticated user in a session and provide a session cookie back to the browser. This authenticates the user in future requests. To log a user out you just remove the user from the session.
Vapor's Auth package provides everything you need to do both of these scenarios. See https://github.com/raywenderlich/vapor-til for examples of both

IBM Worklight : WL.Client.getUserName Fails to retrieve userIdentity immediately after authentication

I have done adapter based authentication and there is no problem in authentication and it works fine. I have faced some issues in getting the active users useridentity.The code may explain you a bit more
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
if (response.responseJSON.errorMessage)
alert(response.responseJSON.errorMessage);
} else if (authRequired == false){
adapterAuthRealmChallengeHandler.submitSuccess();
setTimeout(function(){pageTransitionCall();},10000); //this code only works
pageTransitionCall(); //This throws null error in console
}
};
function pageTransitionCall(){
console.log(WL.Client.getUserName("AdapterAuthRealm"));
}
As you can see i was trying to get the active userName of the realm. The WL.Client.getUserName("AdapterAuthRealm") only works after some time interval only and i am not sure about the time interval. By adapter code is as below
function submitAuthentication(username, password,userCred){
if (username==="worklight" && password === "worklight"){
WL.Logger.info("if");
var userIdentity = {
userId: userCred,
displayName: userCred,
attributes: {
foo: "bar"
},
loginName : userCred,
userName : userCred
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
WL.Logger.info(JSON.stringify(userIdentity));
return {
authRequired: false
};
}
else
{
WL.Logger.info("else");
return onAuthRequired(null, "Invalid login credentials");
}
}
My doubt is why does the client cant retrieve the activeuser. And i am sure that my code is correct and active user is set and i can see in the server log.After the setactvieruser is set only i have return false in the adpter and why cant the client retrieve the user at instant and why it needs delay to retrieve. i have verified in both Worklight V6.0 and also Worklight V6.1.i have created the Ipad environment.
The info that contains logged in userId (basically any userIdentity data) is not returned immediately after adapter authentication but only when an original request succeeds. Consider this
You're making request#1 to the server (let's say invoke procedure)
You're getting response with authRequired:true
You're submitting auth data
You're getting authRequred:false
You're calling submitSuccess()
WL framework automatically re-invokes request#1
You're getting response for request#1
userIdentity data will be returned in step7 and not in step4. Basically once you start authentication flow you're considered out of the original invocation context. You need to finish the flow and tell WL framework that auth has completed. Once you do - WL framework will reinvoke the original request. WL server add userIdentity data to the response and WL client will update userName, displayName etc properties.
In case you need user data before that, e.g. right away once auth is complete, you can add custom properties to your submitAuthentication function response, e.g.
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false,
loginName: userIdentity.loginName
};
this will make sure that loginName will be returned to your handleChallenge function. you can retrieve it there and do whatever you want with it.