Firebase AppCheck on iOS: 403 permission errors - PERMISSION_DENIED - ios

Q:
How can permission errors be resolved for Firebase App Check?
Background:
I have enabled App Check per the documents:
DeviceCheck is enabled/configured per:
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
App Attest is enabled configured per:
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
SDK is added to the project, with code from:
https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift
Specifically, in appdelegate:
Token setup:
FirebaseApp.configure()
requestDeviceCheckToken()
requestDebugToken()
if #available(iOS 14.0, *) {
requestAppAttestToken()
}
calling:
// MARK: App Check providers
func requestDeviceCheckToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
DeviceCheckProvider(app: firebaseApp)?.getToken { token, error in
if let token = token {
print("DeviceCheck token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("DeviceCheck error: \((error as NSError).userInfo)")
}
}
}
func requestDebugToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
if let debugProvider = AppCheckDebugProvider(app: firebaseApp) {
print("Debug token: \(debugProvider.currentDebugToken())")
debugProvider.getToken { token, error in
if let token = token {
print("Debug FAC token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("Debug error: \(error)")
}
}
}
}
#available(iOS 14.0, *)
func requestAppAttestToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
guard let appAttestProvider = AppAttestProvider(app: firebaseApp) else {
print("Failed to instantiate AppAttestProvider")
return
}
appAttestProvider.getToken { token, error in
if let token = token {
print("App Attest FAC token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("App Attest error: \(error)")
}
}
}
requestDeviceCheckToken()returns a permissions error:
DeviceCheck error: ["NSLocalizedFailureReason": The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDeviceCheckToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"service": "firebaseappcheck.googleapis.com",
"consumer": "projects/<my project #>"
}
}
]
}
}
requestDebugToken() returns a permissions error:
Debug error: Error Domain=com.firebase.appCheck Code=0 "The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/<my project #>",
"service": "firebaseappcheck.googleapis.com"
}
}
]
}
}
" UserInfo={NSLocalizedFailureReason=The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/<my project #",
"service": "firebaseappcheck.googleapis.com"
}
}
]
}
}
}
requestAppAttestToken() returns an error:
App Attest error: Error Domain=com.firebase.appCheck Code=0 "(null)"
GCP Console does show all calls to the following w/ 100% errors:
google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDebugToken
google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDeviceCheckToken
google.firebase.appcheck.v1beta.TokenExchangeService.GenerateAppAttestChallenge
All of which seem to point to a permissions error? Specifically, GOOGLE_APP_ID is in the request URL, but App Check is configured in Firebase via the console...
I'm not seeing anything in the docs or anything obvious in IAM that I missed? :(
Ty in advance for help!
Update
After further testing w/ Postman:
The issue seems to be that the SDK isn't passing the X-Ios-Bundle-Identifier correctly when calling AppCheck API(s).
Steps to get to this conclusion:
From POSTMAN: API call w/ original API_KEY -> yields initial (above) error response/403
From POSTMAN: API call as above, + X-Ios-Bundle-Identifier + valid debug_token -> yields success payload.
So:
any ideas to help ID why the X-Ios-Bundle-Identifier isn't being passed by the SDK? The app is using other Firebase API's w/out issue, so seems limited to the AppCheck SDK...
and/or - can the X-Ios-Bundle-Identifier be programmatically added (in Swift) to the AppCheck calls (it is properly notated in the .plist)
Resolved!
App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.
Hopefully, the Application Restriction(s) will be supported at some point...
Update!
v8.8.0-beta now supports the bundle ID! :)

1. Configure the private key for DeviceCheck
Make sure you have created a private key for DeviceCheck
And installed it in firebase project settings under AppCheck tab
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
2. Add debug token to firebase.
If you use AppCheckDebugProvider (basically for simulators), after run the project you will see a debug token in the console, you need to copy it and add to AppCheck of the project settings. Than AppCheck will approve it. Also don't forget to add -FIRDebugEnabled for the Arguments Passed on Launch.
https://firebase.google.com/docs/app-check/ios/debug-provider
3. Add production entitlements for AppAttest environment.
The beta version of AppCheck doesn't work with the AppAttest development environment, so you need to setup the production environment in your entitlements. By default, AppAttest works in a development environment, and regardless of your choice in the market it will work with a production.
https://firebase.google.com/docs/app-check/ios/app-attest-provider
https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
4. Optional:
You can simplify the code
#if targetEnvironment (simulator)
let providerFactory = AppCheckDebugProviderFactory ()
#else
let providerFactory = CustomAppCheckProviderFactory ()
#endif
AppCheck.setAppCheckProviderFactory (providerFactory)
And getting a token
if let fbApp = FirebaseApp.app () {
providerFactory.createProvider(with: fbApp)?.getToken { token, error in
if let token = token {
print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
} else if let error = error {
print ("AppCheck error: \ (error as NSError).userInfo)")
}
}
}
Or if you want to protect non-firebase resources, you can get a token like this:
AppCheck.appCheck().token (forcingRefresh: false) { token, error in
if let token = token {
print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
} else if let error = error {
print ("AppCheck error: \ (error as NSError).userInfo)")
}
}
https://firebase.google.com/docs/app-check/ios/custom-resource

App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.
Hopefully, the Application Restriction(s) will be supported at some point...

Got this error - here is what worked for me:
Run app on real Android device
Opened Android Studio → Logcat → search for “DebugAppCheckProvider” → copy the debug secret
In Firebase Go to “App Check” → Apps → 3 dot menu → Manage debug token → Add token → name it → paste the debug secret
Add console log of the token after activation of app check.
try {
await firebase.appCheck().activate("ignored", true);
const token = await getAppCheckToken();
console.log({ token });
} catch (err) {
console.error(err);
}
};

Related

Amplify ios signin with custom flow

I am trying to implement a custom signin flow using amplify ios library and cognito.
The flow is based on this passwordless implementation https://github.com/mobilequickie/amplify-passwordless-sms-auth/tree/68152489152e1fc4c3185f4e5e3383639bdc8285, it works great on web, but I can't make it work on ios, I get the following error:
-------Sign In response---------
failure(AuthError: Incorrect username or password.
Recovery suggestion: Check whether the given values are correct and the user is authorized to perform the operation.)
Please find below the relevant code:
public init(_ secureService: SecureServiceProtocol) {
self.secureService = secureService
self.token = secureService.get(tokenKey)
self.authModel = secureService.get(authKey, type: AuthModel.self)
do {
let url = Bundle.main.url(forResource: "amplifyconfiguration", withExtension: "json")!
let configuration = try AmplifyConfiguration(configurationFile: url)
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure(configuration)
if authModel != nil {
self.retrieveAuthData { _ in }
}
} catch {
L.log(type: .error, message: error.localizedDescription)
print(error)
print(error.asAFError)
}
}
public func accessWith(_ phone: String, callback: #escaping AuthResultCallback) {
print(phone)
Amplify.Auth.signIn(username: phone) { result in
print("-------Sign In response---------")
print(result)
}
}
configuration
{
"auth": {
"plugins": {
"awsCognitoAuthPlugin": {
"IdentityManager": {
"Default": {}
},
"CredentialsProvider": {
"CognitoIdentity": {
"Default": {}
}
},
"CognitoUserPool": {
"Default": {
"Region": "eu-west-2",
"PoolId": "eu-west-2xxxxxx",
"AppClientId": "5vmjioxxxxxxxxxx"
}
}
},
"Auth": {
"Default": {
"authenticationFlowType": "CUSTOM_AUTH"
}
}
}
}
}
I have been facing the same issue and found this
The root cause for our issue was that the iOS Amplify library always sends an initial ChallengeName of SRP_A to the Cognito signIn call. However, the example "Define Auth Challenge trigger" is explicitly coded to fail any authentication calls where the ChallengeName is not CUSTOM_CHALLENGE.
So you need to port that same behavior with these lambdas. Because the Define lambda looks for the CUSTOM_CHALLENGE ChallengeName and fails requests that have a different ChallngeName, the logic is incompatible with the iOS Amplify libraries as-is, since they initially send SRP_A.
I was able to work around this by modifying the Define Auth Challenge lambda to respond with the CUSTOM_CHALLENGE name instead of failing outright, and that seems to have fixed up the iOS side.
You can use the lambda's from here

Swift, Firebase Cloud Function - INVALID ARGUMENT error

I am trying to call a cloud function from firebase by using the following code.
Client code -
func checkUserStatus() {
let functions = Functions.functions(region: "us-central1")
let argument = [
"currentUser":
[
"email": "test#email.com",
"uid": "LP8R4yZroyMTj"
]
]
functions.httpsCallable("subscriptionStatus").call(argument) { (result, error) in
if error != nil {
print("FAILED")
print(error)
} else {
print("PASSED")
print(result)
}
}
}
Cloud Function code -
exports.subscriptionStatus = functions.https.onRequest(async (request: Request<RequestBody>, response) => {
const {
currentUser,
} = request.body
// Logic goes here
}
But getting the following error when running it
Error Domain=com.firebase.functions Code=3 "INVALID ARGUMENT" UserInfo={NSLocalizedDescription=INVALID ARGUMENT}
The function takes in a parameter called currentUser which further comprises of user's email and uid.
Any lead would be highly appreciated on the matter.
Just to have an answer to the question for anyone else having a similar issue.
To call an onRequest() cloud function you need to use the URL where it's deployed at i.e. https://us-central1-<project-id>.cloudfunctions.net/<function-name>?<var-name>=<var-value>
If you're wanting to call it in the client app using call, then you'll need to use an onCall() cloud function.
Firebase has a one of the best documentation on their services: https://firebase.google.com/docs/functions/get-started
OnCall functions:
https://firebase.google.com/docs/functions/callable
onRequest functions:
https://firebase.google.com/docs/functions/http-events

linkedin get profile - Request failed: forbidden (403) error swift

I am facing problem with "https://api.linkedin.com/v2/me" api of LinkedIn. Code was working good when I was using v1 api. I have updated my code for version 2 api for linkedIn authentication and When I tried to get profile with api "https://api.linkedin.com/v2/me" I am getting error Request failed: forbidden (403). I don't know how to resolve it.
Here is my code:
let linkedinHelper = LinkedinSwiftHelper(configuration: LinkedinSwiftConfiguration(clientId: Constant.Key.kLinkedInClientId, clientSecret: Constant.Key.kLinkedInClientSecret, state: Constant.Key.kLinkedInState, permissions: ["r_basicprofile", "r_emailaddress"], redirectUrl: Constant.Key.kLinkedInRedirectURL),nativeAppChecker: WebLoginOnly())
linkedinHelper.authorizeSuccess({ (token) in
print(token)
let url = "https://api.linkedin.com/v2/me"
linkedinHelper.requestURL(url, requestType: LinkedinSwiftRequestGet, success: { (response) -> Void in
print(response)
}) {(error) -> Void in
print(error.localizedDescription)
//handle the error
}
I've set URL scheme in info.plist as well.
You have to pass key in oauth2_access_token
Example:
https://api.linkedin.com/v2/me?oauth2_access_token={linkedin_key}
Edit:-
Also in permission need to set "r_liteprofile" instead of "r_basicprofile". To change permission worked for me.

Error code 403 when using guest authentication with TwitterKit iOS for getting user timeline

I am using Fabric's twitter kit for getting a username's tweets in my iOS application by making a request to the REST API endpoint "https://api.twitter.com/1.1/statuses/user_timeline.json"
I am have correctly set up my "consumer key" and "consumer secret key" as provided by the Fabric app in my AppDelegate and info.plist , but I repeatedly get the following error message -
Error: Optional(Error Domain=TwitterAPIErrorDomain Code=200 "Request
failed: forbidden (403)"
UserInfo={NSErrorFailingURLKey=https://api.twitter.com/1.1/guest/activate.json,
NSLocalizedDescription=Request failed: forbidden (403),
NSLocalizedFailureReason=Twitter API error : Forbidden. (code 200)})
My code is as under follows -
Twitter.sharedInstance().startWithConsumerKey(TWITTER_CONSUMER_KEY, consumerSecret: TWITTER_CONSUMER_KEY_SECRET)
Fabric.with([Twitter.sharedInstance()])
let userId = Twitter.sharedInstance().sessionStore.session()?.userID
let client = TWTRAPIClient.init(userID: userId)
let params = ["screen_name": twitterUsername, "count" : "10"]
var clientError : NSError?
let request = client.URLRequestWithMethod("GET", URL: TWITTER_TIMELINE_ENDPOINT, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if(connectionError == nil) {
self.twitterJson = self.nsdataToJSON(data!)!
self.constructTweetView(self.twitterJson)
}
else {
print("Error: \(connectionError)")
}
I am on the most recent version of TwitterKit(>2.0)
How can I go about resolving this ?
Thanks!
Add key in exception domains as shown in info.plist. Following fixed bug for me.

Pinterest login through app

Whenever I try to authenticate with app the following msg comes:
{"status": "failure", "code": 283, "host": "coreapp-ngapi-prod-7c03bdd4", "generated_at": "Thu, 06 Aug 2015 08:02:11 +0000", "message": "The authorization grant is invalid", "data": "Invalid Application ID"}
How can I solve it?
The message is pretty explicit: Invalid Application ID.
Have you added the URL Type?
Using this initializer:
PDKClient.configureSharedInstanceWithAppId("0000")
and this OAuth:
let permission = [PDKClientReadPublicPermissions]
PDKClient.sharedInstance().authenticateWithPermissions(permission,
withSuccess: { (pdk :PDKResponseObject!) -> Void in
println("success PDKResponseObject: \(pdk)")
}) { (err :NSError!) -> Void in
println("error NSError: \(err)")
}
and assuming your client_id is correct, you should be in business.

Resources