Twilio Invalid Access Token Signature (iOS - Swift) - twilio

I am using Twilio's latest SDK they released on CocoaPods as of today. I am trying to implement VOIP feature to my app with Twilio Programmable Voice. My backend is .net which also uses the latest release of Twilio Helper Library for C#.
My client code looks like:
fetchAccessToken { (accessToken: String) in
TwilioVoice.register(withAccessToken: accessToken, deviceToken: deviceToken) { (error) in
if let error = error {
NSLog("An error occurred while registering: \(error.localizedDescription)")
}
else {
NSLog("Successfully registered for VoIP push notifications.")
}
}
}
What I get in the console is as following:
voipTestWithTwilio[2431:517236] [ERROR TwilioVoice] Inside register:deviceToken:completion:, failed to register for Twilio push notifications. Error:Invalid access token signature
voipTestWithTwilio[2431:517236] An error occurred while registering: Invalid access token signature
This is the C# code that actually creates the token:
var grant = new VoiceGrant
{
OutgoingApplicationSid = outgoingApplicationSid
};
var grants = new HashSet<IGrant> { { grant } };
var token = new Token(
accountSid: accountSid,
signingKeySid: apiKey,
secret: apiSecret,
identity: identity,
grants: grants
);
return token.ToJwt();
I have been looking for the issue on the internet, nothing helped so far. I have tried contacting them but have not got any response back. I also tried creating new api keys and even a new project for a couple times on Twilio. Can anybody say something about the issue?
UPDATE
I added push notification sid to VoiceGrant and now I’m getting 403 Forbidden.
On Twilio error codes page it is explained as: “The expiration time provided in the Access Token exceeds the maximum duration allowed.” which is definitely not my case. However, I tried passing expiration parameter in Token constructor with various values which didn’t change the result.
The problem is still persisting.

I solved the issue. It was because my server returned the token with quotation mark.
I remember print(token)'ing on client (iOS) to see whether there is encoding issue or something and all I see was a proper token between quotation marks. Since token is a string value, I didn't pay attention to quotation part of it. That's where I was wrong.

Related

iOS Google SignIn refreshed idToken has missing profile info in backend authentication

I use GoogleSignIn for iOS (GoogleSignIn-iOS), v6.1.0, in my iOS app.
All calls to my backend have the idToken in the request header.
The id token is verified in the backend. Here I also need to retrieve the users email and name.
(see also: https://developers.google.com/identity/sign-in/ios/backend-auth)
After a new SignIn with GIDSignIn.sharedInstance.signIn everything works fine.
GIDSignIn.sharedInstance.currentUser.profile contains email and name.
When sending the idToken to the backend, the Verifier gives me name and email in its payload, too.
Before I do a backend request, I get a valid (=not expired) idToken, with the following code:
private static func refreshToken(_ authentication: GIDAuthentication) async throws -> GIDAuthentication {
try await withCheckedThrowingContinuation { continuation in
authentication.do { authentication, error in
if let authentication = authentication {
continuation.resume(returning: authentication)
} else if let error = error {
Log.warn("Google SignIn refreshToken failed with -> \(error)")
continuation.resume(throwing: error)
}
}
}
}
I use the following code to get the idToken, before I create the request for my URLSession.
func idToken() async -> String {
do {
guard let user = GIDSignIn.sharedInstance.currentUser else {
Log.error("No GID user to get idToken from")
return ""
}
currentAuth = try await Self.refreshToken(user.authentication) //currentAuth is a class variable
return currentAuth?.idToken ?? ""
} catch {
print("Error during Google SignIn idToken retrieval \(error)")
return ""
}
}
And now my problem comes:
The idToken is refreshed properly. It is valid for another hour, and the verifier in my backend accepts it.
But I can't get the users name from the verified payload data in the backend, the name field is null.
Same happens when I use GIDSignIn.sharedInstance.restorePreviousSignIn (which I call on every app re-start, to do the silent sign in. (But in the app, the values are there in the updated users object profile)
It seems to me, that when the idToken gets refreshed, that it looses the profile scope.
I hope someone can help me with this, or at least explain the behaviour to me.
Thank in advance :)
Update
I checked the idTokens on https://jwt.io.
They are valid, but after the refresh, the jwt payload definitely is missing the profile data, like the users name.
I waited one day and tried again. Now the silent signin after app start gives me a complete idToken with jwt payload including name, but only once. After an hour, when the idToken gets refreshed, the idToken is again missing the profile information
Unfortunately I got no hint here, so I solved my problem as follows.
I hope this approach can save time for some others in the future.
I only require the profile data, when the user logs in to the backend the first time and a new user record is created in the backend.
In all other calls, where I need the JWT for authentication, I only rely on the basic information (ID, email) and handle all other values as optional values.
So I check the users name, if it is available. Otherwise the ID and a valid token is of course sufficent for authentication.

AWS Amplify iOS SDK : FederatedSignIn Failed to retrieve authorization token on Amplify.API.post

I've been working with the Amplify SDK to get federatedSignIn working with my iOS app with "Sign in with Apple" and Cognito to eventually make calls to API Gateway / Lambda functions.
TL;DR : My access token does not appear to be "automatically included in outbound requests" to my API as per the last paragraph of this section of the docs : Cognito User pool authorization
I have successfully authenticated using the tutorial found here Authentication Getting Started and other various Youtube videos on the Amazon Web Services channel.
Upon successful sign in through Apple I'm given an ASAuthorizationAppleIDCredential object. This contains the user field (token) which I pass to the Amplify.Auth class using the following Swift code :
func signIn (with userId: String)
{
guard
let plugin = try? Amplify.Auth.getPlugin(for: AWSCognitoAuthPlugin().key),
let authPlugin = plugin as? AWSCognitoAuthPlugin,
case .awsMobileClient (let client) = authPlugin.getEscapeHatch()
else
{
return
}
client.federatedSignIn(providerName: AuthProvider.signInWithApple.rawValue, token: userId) { (state, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
else if let unwrappedState = state
{
print ("Successful federated sign in:", unwrappedState)
}
}
}
All appears to be successful and to double check I use the following bit of code to ensure I'm authorized :
func getCredentialsState (for userId:String)
{
let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: userId) { (credentialsState, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
switch credentialsState
{
case .authorized:
print ("User Authorized")
case .notFound, .revoked:
print ("User Unauthenticated")
case .transferred:
print ("User Needs Transfer")
#unknown default:
print ("User Handle new use cases")
}
}
}
In the console I see "User Authorized" so everything appears to be working well.
However when I then go to make a call to Amplify.API.post I get the following error:
[Amplify] AWSMobileClient Event listener - signedOutFederatedTokensInvalid
Failed APIError: Failed to retrieve authorization token.
Caused by:
AuthError: Session expired could not fetch cognito tokens
Recovery suggestion: Invoke Auth.signIn to re-authenticate the user
My function for doing the POST is as follows :
func postTest ()
{
let message = #"{'message": "my Test"}"#
let request = RESTRequest (path: "/test", body: message.data(using: .utf8))
Amplify.API.post (request:request)
{
result in switch result
{
case .success(let data):
let str = String (decoding: data, as: UTF8.self)
print ("Success \(str)")
case .failure(let apiError):
print ("Failed", apiError)
}
}
}`
I then went into the API Gateway UI and changed the generated Method Request on my resource from AWS IAM to my Cognito User Pool Authorizer thinking this was the issue. I also changed the awsAPIPlugin authorizationType to "AMAZON_COGNITO_USER_POOLS" in my amplifyconfiguration.json file. This unfortunately did not have any affect.
I've seen posts such as this issue User is not created in Cognito User pool for users logging in with Google federated login #1937 where people discuss the problem of having to to use a web ui to bring up the social sign in. I understand that Apple will reject your app sometimes for this. Therefore this is not a solution.
I then found this post which seems to resolve the issue however this appears to use the old version of the SDK? Get JWT Token using federatedSignIn #1276
I'm not great with Swift (I'm still an Objective C expert, but am slowly learning Swift) so I'm uncertain which path to go here and whether this is actually a solution? It does seem to be quite more complicated than the function I have that does my POST? The RESTRequest does seem to be a simple and easy solution but I'm uncertain how to pass it the Authorization token (or even how to get the token if it is needed here).
However, everything I've read about the SDK is that the authorization should be handled automatically in the background according the docs in my first link above. Specifically pointed out, again, here : Cognito User pool authorization. The last paragraph here states 👍
With this configuration, your access token will automatically be included in outbound requests to your API, as an Authorization header.
Therefore, what am I missing here as this does not appear to automatically include my access token to my outbound requests to my API?

My android project crashed when I called token.jwt for twilio chat

I am trying to generate access token for twilio chat but got this error:I have been trying to figure out where the error is coming from but can't get it figured out. I will really appreciate your help. Thanks
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.zihron.projectmanagementapp, PID: 16355
java.lang.Error: javax.xml.datatype.DatatypeConfigurationException: Provider org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl not found
at javax.xml.bind.DatatypeConverterImpl.<clinit>(DatatypeConverterImpl.java:744)
at javax.xml.bind.DatatypeConverter.<clinit>(DatatypeConverter.java:78)
at javax.xml.bind.DatatypeConverter.printBase64Binary(DatatypeConverter.java:547)
at io.jsonwebtoken.impl.Base64Codec.encode(Base64Codec.java:24)
at io.jsonwebtoken.impl.Base64UrlCodec.encode(Base64UrlCodec.java:22)
at
io.jsonwebtoken.impl.AbstractTextCodec.encode(AbstractTextCodec.java:31)
at io.jsonwebtoken.impl.DefaultJwtBuilder.base64UrlEncode(DefaultJwtBuilder.java:314)
at io.jsonwebtoken.impl.DefaultJwtBuilder.compact(DefaultJwtBuilder.java:282)
at com.twilio.jwt.Jwt.toJwt(Jwt.java:100)
at ZihronChatApp.token.TokenGenerator.getToken(TokenGenerator.java:34)
at com.zihron.projectmanagementapp.ChatActivity.onCreateView(ChatActivity.java:43)
I have my details below:
public AccessToken getToken() {
// Required for all types of tokens
String twilioAccountSid ="AC601f2c7***7ed***640***264c***d0d";
String twilioApiKey = "SK684***dda***c81****6c4a****093**";
String twilioApiSecret ="96****dbc06****b74d50***b9***3*4";
String serviceSid="IS***a29****e24****5d****4b20**3e*";
String identity = "joshua.hamilton#gmail.com";
ChatGrant grant = new ChatGrant();
grant.setServiceSid(serviceSid);
AccessToken token = new AccessToken.Builder(twilioAccountSid,
twilioApiKey, twilioApiSecret)
.identity(identity).grant(grant).build();
Log.e("++==--",""+token.toJwt());
//.identity(identity).grant(grant);
return token;
}
Twilio developer evangelist here.
The Twilio Java library is not intended for use within Android projects.
The issue here is that you should not be storing your credentials within your application. A malicious user could decompile your application, take your credentials and abuse them.
Instead, you should create a server (or use some sort of serverless environment, like Twilio Functions) that can run this code and return the token. You should then make an HTTP request from your Android application to fetch that token. Check out the Twilio Programmable Chat Android Quickstart to see how it's done there.

Incomplete Linkedin OAuth 2.0 access token response

My question is about OAuth2 access token response from Linkedin api. When I'm trying to get this token I recieve the following response:
{"access_token":"...","expires_in":...}
But the thing is that according to OAuth2 documentation (in 5.1 paragraph) there should be at least one more required parameter - "token_type".
So the question is: could it be somehow customized so the linkedin API will return this parameter with access token response or it is just a departure from the rule and this parameter won't be returned?
Thanks in advance.
I have run into the same issue. According to LinkedIn Docs:
A successful Access Token request will return a JSON object containing the following fields:
access_token — The access token for the user. This value must be kept secure, as per your agreement to the API Terms of Use.
expires_in — The number of seconds remaining, from the time it was requested, before the token will expire. Currently, all access tokens are issued with a 60 day lifespan.
they respond with
{"access_token":"...","expires_in":...}
which violates the standard.
Currently I am using Spring Security 5.0.3 and to fix the issue, I had to monkeypatch one class:
com.nimbusds.oauth2.sdk.token.BearerAccessToken
I will not post the whole class, only a significant part:
public static BearerAccessToken parse(final JSONObject jsonObject)
throws ParseException {
// Parse and verify type
AccessTokenType tokenType;
try {
tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
} catch (ParseException ex) {
tokenType = AccessTokenType.BEARER;
}
if (!tokenType.equals(AccessTokenType.BEARER))
throw new ParseException("Token type must be \"Bearer\"");
//...
}
I hoped to get answer from Linkedin member since they stated on their site that stackoverflow is a proper place for asking such questions. But since there is no answer from them and I didn't find any relevant information regarding this question I believe that it is just the way they implemented OAuth 2.0 protocol.

Facebook iOS SDK - extend expired token

I am having hard time trying to get the new extending-an-expired-token flow work.
When first-time user sign in the app over Facebook SSO, the token returned by Facebook usually expires in roughly 2 hours.
Facebook iOS SDK documentation makes the case simple.
extend expired token
So, I called "extendAccessTokenIfNeeded" according to the documentation. However, token refresh never happens. In the iOS SDK source, there's a global int const "kTokenExtendThreshold" set to 24 (hours).
My "extendAccessTokenIfNeeded" never invokes the real extend token logic.
To do a test, I called "extendAccessToken" instead.
Facebook returns the following error on this call:
Error Domain=facebookErrDomain Code=101 "The operation couldn’t be completed. (facebookErrDomain error 101.)" UserInfo=0x87e8820 {request_args=(
{
key = method;
value = "auth.extendSSOAccessToken";
},
{
key = format;
value = json;
},
{
key = sdk;
value = ios;
},
{
key = "sdk_version";
value = 2;
}
), error_msg=Invalid API key, error_code=101}
What the hell is going on?
I really appreciate any tips and comments. Is it that I missed something?
Thanks
Bill

Resources