Expo Notification on Android - expo

I have setup the expo push notification in my app. I am able to get the device token for both android and ios in my expo go app. when i publish my app to test-flight i am able to get the device token as well. But when i publish the app to PLaystore for testing i notice the device token is not working because i am saving the token to my laravel backend.
below is my code
const registerForPushNotificationsAsync = async () => {
let token;
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
sendDeviceTokenToBackend(token)
} else {
alert("Must use physical device for Push Notifications");
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
return token;
};
Below is the code i use to send to my backend
const sendDeviceTokenToBackend=(token)={
const obj = {
device_token: data,
};
const response = await db.communicate(
"POST",
"/device/tokens",
obj,
"auth"
);
}
i read a couple of answers on stackoverflow that i need to configure FCM in order for my android noticifation to work. but here i will be using php sdk for my notification configuration so i am saving the device token to the backend. so i have a few questions
if i am to use firebase to save my device token how will i send my device token to my php backend from firebase .
since i will be managing it on my php server instead of expo push notification how do i get the token in andriod when i publish it to playstore
any help on this i will appreciate

Related

AsyncStorage.setItem is not working with Expo BackgroundFetch

using "expo" (~43.0.2) and "expo-background-fetch" (~10.0.3), my app need to download update from server then save it into storage (AsyncStorage is simply used), but failed. here is my code snippet
TaskManager.defineTask("test", async () => {
const token = await AsyncStorage.getItem("token");
const res = await fetch("...", { headers: { "authorization": token } }) // download data from server
const data = await res.json();
await AsyncStorage.setItem("unread", `${data?.count ?? 0}`);
return BackgroundFetch.BackgroundFetchResult[+data?.count ? "NewData" : "NoData"]
})
I find that the AsyncStorage.getItem is working because there is a valid server log. However, i cannot retrieve the unread from AsyncStorage in the app.
can someone help? any suggestion to me?
moreover, i need to call expo.pedometer.getStepCountSync and scheduleNotificationAsync during the background task, is it possible?

Firebase OAuthProvider oidc (keycloak) invalid nonce

Here using keycloak to get idToken
Getting error when using signInWithCredential in firebase
Error
The nonce in ID Token "68963ae6-e032-42b4-a7b1-5672f053acf5" does not match the SHA256 hash of the raw nonce "68963ae6-e032-42b4-a7b1-5672f053acf5" in the request.
const provider = new OAuthProvider('oidc.inspect-app');
const auth = getAuth();
const credential = provider.credential({idToken:orginApp.idToken, rawNonce:
orginApp.tokenParsed.nonce});
signInWithCredential(auth, credential)
.then((result:any) => {
//code logic
})
})
I had to switch to code flow to make it work.
In this example i used ionic and OAuth2Client (Capacitor Plugin) to retrieve credentials after native signIn for ios and android.
public async signIn() {
// Ionic Capacitor Plugin
const result = await OAuth2Client.authenticate(OAUTH_OPTIONS);
const provider = new OAuthProvider('oidc.your-keycloak-provider');
provider.addScope('email');
provider.addScope('profile');
provider.addScope('openid');
const credential = provider.credential({
accessToken: result.access_token_response['access_token'],
idToken: result.access_token_response['id_token']
});
return signInWithCredential(this.auth, credential);
}

AWS Cognito JWT will not pass validation with .net core api, missing cognito configuration?

I am trying to attach an angular application to a .NET core API utilizing the JWT token. At this point i have the local angular app authenticating with Cognito and getting the user account.
I've followed this to get the token attached to the request.
https://medium.com/#umashankar.itn/aws-cognito-hosted-ui-with-angular-and-asp-net-core-5ddf351680a5
Amplify.Configure({
Auth: {
region: 'us-west-2',
userPoolId: 'us-west-MY POOL',
userPoolWebClientId: 'MY APP CLIENT ID'
}
}
});
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (request.url.indexOf(environment.api.baseUrl) == 0) {
return this.getToken().pipe(mergeMap(token => {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(request);
}));
}
return next.handle(request);
}
}
getToken() {
return from(
new Promise((resolve, reject) => {
Auth.currentSession()
.then((session) => {
if (!session.isValid()) {
resolve(null);
} else {
resolve(session.getIdToken().getJwtToken());
}
})
.catch(err => {
return resolve(null)
});
})
);
}
And i can confirm that it is adding the token to the request.
Interesting thing to note is that i'm using the session.getIdToken().getJwtToken() but there also is session.getAccessToken().getJwtToken() and they are different. I can't find anything telling me what the difference is, but i've tried both and they both have the same issue.
For the server side i've followed this answer to setup the .net core site and i can confirm that it is appropriately downloading the keys from /.well-known/jwks.json. It however just keeps rejecting the request with authentication failure.
How to validate AWS Cognito JWT in .NET Core Web API using .AddJwtBearer()
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
// get JsonWebKeySet from AWS
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
// serialize the result
return JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
},
ValidateIssuer = true,
ValidIssuer = $"https://cognito-idp.us-west-2.amazonaws.com/us-west-MYID",
ValidateLifetime = true,
LifetimeValidator = (before, expires, token, param) => expires > DateTime.UtcNow,
ValidateAudience = true,
ValidAudience = "MY APP CLIENT ID"
};
});
app.UseAuthentication();
[HttpGet]
[Authorize]
public IEnumerable<Device> Get()
{
return 'my devices...';
}
The angular app is running at http://localhost:4200 and the .net core is running at https://localhost:44300.
So the question i have is, am i missing some sort of setup in my cognito app client? What am i missing to get the .NET core app to take the JWT?
Turns out i actually did have everything correct as far as Cognito goes.
What i did have was this.
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Which is not the correct order for things to work... this is..
app.UseRouting();
app.UseAuthentication(); <-- Authentication before authorization
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

Expo, React Native, Share/upload picture to Facebook (story on my own page)

I am really lost here, I have an Expo React-Native application. the user can login with Facebook credentials.
loginWithFacebook = async () => {
const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync(FACBOOK_APP_ID, {
permissions: ['public_profile', 'email'],
});
if (type === 'success') {
this.callGraph(token);
} else {...});
}
};
and
callGraph = async (token) => {
const response = await fetch(`https://graph.facebook.com/me?access_token=${token}&fields=id,name,email,picture`);
const responceJSON = JSON.stringify(await response.json());
try {
await AsyncStorage.multiSet([['FBResponse', responceJSON], ['FBToken', token]]);
} catch (error) {...}
};
now I have the Token and info of the loged in user. now I get a picture from cameraRoll
pickImage = async () => {
const result = await Expo.ImagePicker.launchImageLibraryAsync({...});
};
which gives the URI of the image at the device, also I am using RNFetchBlob to again have the image as base64.
but I am really confused that how should I share/upload this picture to Facebook. Should I eject from Expo install FBSDK and do it from Android folder? or I can do it with the Graph API? or I can Link it to Expo?
I could not find a single example, just simple react-native-share samples are what I see everywhere.
Facebook integration for Expo only accepts read permissions and one cannot login with publish_actions it seems the community is still working on it. and for using fbsdk you should either eject from Expo or ask for permissions via the Expo WebBrowser integration.

How to subscribe AWS SNS Topic in React Native (Android)?

I tried with the aws-sdk-react-native module:
https://github.com/awslabs/aws-sdk-react-native
The configuration took some time, but thanks to this links I could i.e. list the topics:
https://github.com/awslabs/aws-sdk-react-native/issues/35
https://github.com/awslabs/aws-sdk-react-native/blob/master/SNS/IntegrationTests/SNSTests.js
The test includes a sample how to subscribe to get emails but not how to get notifications in the app. I don't know how to get the platformEndpoint, the PlatformApplicationArn and the deviceToken.
endPoint = sns.createPlatformEndpoint({
PlatformApplicationArn: '{APPLICATION_ARN}',
Token: '{DEVICE_TOKEN}'
})
...
var subscribeRequest= {
"Protocol":"application",
"TopicArn":topicARN,
"Endpoint":endPoint
}
try{
await AWSSNS.Subscribe(subscribeRequest);
}catch(e){
console.error(e);
shouldResolve = false;
return shouldResolve;
}
Are there any samples for this?
I'm also looking for an authentication sample.
Would it be easier to use firebase?
Thanks
I have used GCM over SNS to send notifications. Here are the steps I went through assuming you already set up GCM and added required libraries from AWS React Native SDK:
First create a SNS app from AWS:
Then you need to create Federated Identity through Cognito service of AWS. This is required for sending your device token from mobile app to AWS SNS app. Choose Manage Federated Identities
Then create your pool, don't forget to check Enable Access to unauthenticated identities
When you create the pool you will need to create IAM Roles for unauthenticated and authenticated roles of that pool. AWS will help you create new roles for that but you need to go to IAM Roles menu and attach AmazonSNSFullAccess to created roles, otherwise from the mobile app you won't able to send device token.
After doing these steps you will able send your device token using Amazon's React Native SDK. I have written a helper class for sending token to AWS SNS as suggested here:
class AWSUtility {
constructor() {
const region = "us-west-1"; //change it with your region
const IDENTITY_POOL_ID = "pool id created from Federated Identities"
AWSCognitoCredentials.initWithOptions({region, identity_pool_id: IDENTITY_POOL_ID});
AWSSNS.initWithOptions({region});
}
addTokenToAWSSNS(token, snsEndpointARN) {
const applicationArn = "change with SNS application Amazon resource name";
return Promise.try(() => {
if (!snsEndpointARN) {
return this.createPlatformEndpoint(token, applicationArn);
} else {
return AWSSNS.GetEndpointAttributes({EndpointArn: snsEndpointARN})
.then((result) => {
const {Attributes = {}} = result;
const {Token, Enabled} = Attributes;
const updateNeeded = Token !== token || Enabled !== 'true';
if (updateNeeded) {
return this.updateEndpoint(token).then(() => result.EndpointArn);
}
return snsEndpointARN;
})
.catch(() => {
this.createPlatformEndpoint(token, applicationArn)
});
}
});
}
updateEndpoint(snsEndpointARN, token) {
//AWS is returning error saying that it requires 6 params to update endpoint, if anyone has any idea about it let me know please
return AWSSNS.SetEndpointAttributes({EndpointArn: snsEndpointARN, Attributes: {Token: token, Enabled: true}});
}
createPlatformEndpoint(token, applicationArn) {
return AWSSNS.CreatePlatformEndpoint({Token: token, PlatformApplicationArn: applicationArn})
.then(result => result.EndpointArn)
.catch((error = {}) => {
console.log(error);
});
}
}
export default new AWSUtility();