SPFx Graph API Response Handling - api

My SPFx webpart is working well until I try to return the result of an Graph API call. The below code is trying to get the users that have permissions to the root file library for the site the webpart is installed.
The console.log("Reached the Graph") shows up in the inspector console so I'm guessing it has something to do with the .push() I'm using to append each returned record to the response declared at the beginning of the method.
I also tried the more documented .forEach() below without success. Any Ideas?
private _loadUser(): MicrosoftGraph.Permission[] {
let response: MicrosoftGraph.Permission[];
this.props.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient): void => {
let apiUrl: string = '/groups/'+this.props.group+'/drive/items/root/permissions';
client
.api(apiUrl)
.version("v1.0")
.get((error?, result?: MicrosoftGraph.Permission[], rawResponse?: any) => {
// handle the response
if(error){
console.error(error);
}
if (result) {
console.log("Reached the Graph");
for (let res of result){
response.push(res);
}
}
}
);
});
return response;
}
result.forEach(element => {
response.push(element);
console.log(element.grantedTo.user.displayName);
});

Related

What are the best practices for handling vuex errors?

I'm new to vue. I use interceptors for handling action responses, all easy with successful responses. But I would like to know what are the best practice to handle error responses.
I want to show a toastr with error message from response by default if there's no catch block in the action, but if there is a catch, do only catch function with no toastr shown.
Also, is it ok to handle unauthorized response making a redirect to login page directly in interceptor and what advices can be given about it?
My current interceptor looks like this:
axios.interceptors.response.use(
(response) => {
return response.data.data;
},
(error: AxiosError) => {
const data = error.response?.data;
const code = data?.code;
if (code === ErrorCodes.NEED_EMAIL_CONFIRMATION) {
router.push("email-verification").then();
} else if (code === ErrorCodes.UNAUTHORIZED) {
router.push("sign-in").then();
} else {
if (undefined !== data.error) {
toaster.error(data.error);
} else {
toaster.error(i18n.t("unexpected"));
}
}
return error;
}
);
but I don't like too many responsibilities here and I don't know how to avoid toastr show when the action has a catch function
You can control error toast notification from where you send the request, by sending an extra config.
Using axios:
axios.post('/api-name', data, {
config: {
showToast: true,
},
})
and then on axios intercept:
axios.interceptors.response.use(
response => {...},
error => {
const showTost= error.config.errorToast
if(showToast){
// show toast you can pass custom message too...<3
}
}

Web app that runs in Microsoft Teams (personal tab) doesn't always work on Desktop version

I have a Web app built in Vuejs and has SSO authentification using microsoftTeams.authentication.getAuthToken when running in Teams, or microsoftAuthLib when running in the browser.
Inside the company's network or when connected to the VPN everything works absolutely fine.
We recently opened it outside of the VPN and we created a public certificate for it. So when I disconnect the VPN, it works:
In any browser (outside of Teams).
Teams browser version.
Teams on Android/iPhone.
But it doesn't work on Teams Windows Desktop version, it fails with the following error:
Refused to display
'https://login.microsoftonline.com/.../oauth2/authorize?...' in a
frame because it set 'X-Frame-Options' to 'deny'.
Anybody has an idea what could be the issue? And why would it work on the company's VPN but not outside?And only on specific cases? I am lost, any help would be appreciated.
Thank you
*** EDIT / ADDED SSO REDIRECT CODE ***
import * as microsoftTeams from "#microsoft/teams-js";
import * as microsoftAuthLib from "msal";
import settings from './settings.js';
var msalConfig = {
auth: {
clientId: settings.sso.id,
authority: settings.sso.authority
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var requestObj = {
scopes: settings.sso.scopes
};
var myMSALObj = new microsoftAuthLib.UserAgentApplication(msalConfig);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function authRedirectCallBack(error, response) {
if (error) {
console.log(error);
} else {
console.log("token type is:" + response.tokenType);
}
}
function loginRedirect(requestObj) {
let account = myMSALObj.getAccount();
if (!account) {
myMSALObj.loginRedirect(requestObj);
return false;
} else {
return true;
}
}
function acquireMsalToken() {
return new Promise(function (resolve) {
resolve(myMSALObj.acquireTokenSilent(requestObj).then(token => {
return token.accessToken;
}).catch(error => {
acquireMsalTokenRedirect(error);
}));
})
}
function acquireTeamsToken() {
return new Promise((resolve, reject) => {
microsoftTeams.authentication.getAuthToken({
successCallback: (result) => {
resolve(result);
},
failureCallback: (error) => {
reject(error);
}
});
});
}
function acquireMsalTokenRedirect(error) {
if (error.errorCode === "consent_required" ||
error.errorCode === "interaction_required" ||
error.errorCode === "login_required") {
myMSALObj.acquireTokenRedirect(requestObj);
}
}
var msal = {
autoSignIn: function () {
return loginRedirect(requestObj);
},
acquireToken: async function () {
if (settings.sso.inTeams) {
microsoftTeams.initialize();
microsoftTeams.enterFullscreen();
return acquireTeamsToken();
} else {
let signedIn = msal.autoSignIn();
if (signedIn) {
return acquireMsalToken();
}
}
}
}
export default msal
This error means that you are trying to redirect your tab's iframe to the AAD login flow which in turn is unable to silently generate an auth token for you and is attempting to show an interactive flow (e.g. sign in or consent):
Refused to display
'https://login.microsoftonline.com/.../oauth2/authorize?...' in a
frame because it set 'X-Frame-Options' to 'deny'.
To avoid this issue you need to try and acquire a token silently and if that fails use the microsoftTeams.authentication.authenticate API to open a popup window and conduct the AAD login flow there.
Replacing the acquireTeamsToken() function with the following resolved the issue.
function acquireTeamsToken() {
return new Promise((resolve, reject) => {
microsoftTeams.initialize(() => {
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/ms-teams/auth-start",
width: 600,
height: 535,
successCallback: (result) => {
resolve(result);
},
failureCallback: (error) => {
reject(error);
}
});
});
});
}
I found this documentation very helpful on how to create the Authentication pop up and how to create a Callback window with the Token in it.
You might also want to cache the token and only create a popup when it expires.
This might be because you're using the auth popup option instead of the redirect option in whichever auth library you're using (hopefully MSAL 2.0). Teams is a little different because it's actually launching a popup for you when necessary, so although it sounds a bit strange, you actually want to use the redirect option, inside the popup that is launched. What might help is to look at the new SSO Sample app in the Teams PnP samples.
Go to: %APPDATA%\Microsoft\Teams
Open the file hooks.json (if it's not there, create it)
Add the following to it: {"enableSso": false, "enableSsoMac": false}
That's it, now Teams desktop has the same authentication workflow as the browser version. Have a nice day.

Standalone Blazor WebAssembly authentication very slow [duplicate]

I would like to set up a Blazor client-side app with authentication through AWS Cognito.
When I run the app I'm not redirected to a login page, instead the page says "Authorizing..." for a few seconds, while I get this error in the console:
The loading of “https://blazorapp.auth.eu-central-1.amazoncognito.com/login?…Q&code_challenge_method=S256&prompt=none&response_mode=query” in a frame is denied by “X-Frame-Options“ directive set to “DENY“.
This error page has no error code in its security info
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
Then, the default "Hello, world!" index page is shown (even though as I understand it, it should not be visible to an unauthenticated user based on App.razor definition?). If I click on "Log in", I get the same error in console, but then after a few seconds the Cognito-hosted login page opens, I am able to log in, I am redirected back to my app, and the app shows the authenticated user's info in top right corner, but the console is a little weird again:
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
Authorization was successful.
Question 1
How can I get rid of these errors and have my app redirect to Cognito login page without ~10s delay?
Question 2
Why is all content in my app visible at all times regardless of whether I'm authenticated or not? It's as if the NotAuthorized node under AuthorizeRouteView in App.razor had no effect at all, unless I am confusing something here
Code:
Program.cs
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = "https://cognito-idp.{aws-region}.amazonaws.com/{cognito-userpoolid}";
options.ProviderOptions.ClientId = "{cognito-clientid}";
options.ProviderOptions.ResponseType = "code";
options.ProviderOptions.RedirectUri = "https://localhost:44306/authentication/login-callback";
options.ProviderOptions.PostLogoutRedirectUri = "https://localhost:44306/authentication/logout-callback";
});
App.razor (as created from template, no modifications)
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
I have only modified the call to AddOidcAuthentication in Program.cs myself, all other files were populated by Visual Studio when creating a Blazor WebAssembly App with Individual User Accounts.
I am struggling to get this to work and would greatly appreciate any help on this topic
EDIT:
Following #aguafrommars's answer I have published the website to Amazon S3 using static website hosting with Amazon CloudFront as CDN, however, the behavior of the published app is exactly the same as described local behavior
To expand on the questions:
Question 1 expanded:
When the page says "Authorizing..." I only get the described error in the console, the Cognito hosted UI is not rendered, only when I click on "Log in" I am either redirected (with major delay) to Cognito hosted UI, or authenticated without redirection (if I signed in before), perhaps this GIF will clear things up:
I might be wrong, but isn't the problem that the Cognito hosted UI is rejecting to be rendered in an iframe? Can my app redirect to the hosted UI in the first place, like it eventually does? Right now I have to wait while X-Frame-Options error is thrown, click on "Log in", wait while another X-Frame-Options error is thrown, and then finally I'm redirected and the flow succeeds (in the gif the UI doesn't show up because I authenticated before in the session)
Question 2 expanded:
The behavior I want to achieve is that if the user is not authenticated, they cannot see any part of the application, instead they are redirected to Cognito hosted UI and only after they are authenticated they can see anything. I tried to play around with Authorize attribute in MainLayout.razor, but the result is always a blank screen, I would like to provide some code and details but I believe the behavior is impacted by errors described in Question 1, which is why I would like to sort it out first
I'm answering this issue which was marked as duplicate here...
The reason for the delay is the timeout waiting for the silent login process (which has a 10s timeout I believe) as mentioned here and here
Root cause is that AWS Cognito is not compliant with the OIDC standards. Which results in the "'X-Frame-Options' to 'DENY'" error in the browser console.
Until the Blazor team allow us to turn off the silent signin from code, the solution was to disable silent login as follows:
Download the Blazor Interop files located in the asp.net repo here to a local folder.
Open the local folder using vs code and install typescript, webpack, yarn, etc if not already installed
npm install -g yarn
npm install -g typescript
npm install -g webpack
Then edit the AuthenticationService.ts file as follows (commenting out the silent signin functionality). Sorry for the long code print.
import { UserManager, UserManagerSettings, User } from 'oidc-client'
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
type ExtendedUserManagerSettings = Writeable<UserManagerSettings & AuthorizeServiceSettings>
type OidcAuthorizeServiceSettings = ExtendedUserManagerSettings | ApiAuthorizationSettings;
function isApiAuthorizationSettings(settings: OidcAuthorizeServiceSettings): settings is ApiAuthorizationSettings {
return settings.hasOwnProperty('configurationEndpoint');
}
interface AuthorizeServiceSettings {
defaultScopes: string[];
}
interface ApiAuthorizationSettings {
configurationEndpoint: string;
}
export interface AccessTokenRequestOptions {
scopes: string[];
returnUrl: string;
}
export interface AccessTokenResult {
status: AccessTokenResultStatus;
token?: AccessToken;
}
export interface AccessToken {
value: string;
expires: Date;
grantedScopes: string[];
}
export enum AccessTokenResultStatus {
Success = 'success',
RequiresRedirect = 'requiresRedirect'
}
export enum AuthenticationResultStatus {
Redirect = 'redirect',
Success = 'success',
Failure = 'failure',
OperationCompleted = 'operationCompleted'
};
export interface AuthenticationResult {
status: AuthenticationResultStatus;
state?: unknown;
message?: string;
}
export interface AuthorizeService {
getUser(): Promise<unknown>;
getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult>;
signIn(state: unknown): Promise<AuthenticationResult>;
completeSignIn(state: unknown): Promise<AuthenticationResult>;
signOut(state: unknown): Promise<AuthenticationResult>;
completeSignOut(url: string): Promise<AuthenticationResult>;
}
class OidcAuthorizeService implements AuthorizeService {
private _userManager: UserManager;
private _intialSilentSignIn: Promise<void> | undefined;
constructor(userManager: UserManager) {
this._userManager = userManager;
}
async trySilentSignIn() {
if (!this._intialSilentSignIn) {
this._intialSilentSignIn = (async () => {
try {
await this._userManager.signinSilent();
} catch (e) {
// It is ok to swallow the exception here.
// The user might not be logged in and in that case it
// is expected for signinSilent to fail and throw
}
})();
}
return this._intialSilentSignIn;
}
async getUser() {
// if (window.parent === window && !window.opener && !window.frameElement && this._userManager.settings.redirect_uri &&
// !location.href.startsWith(this._userManager.settings.redirect_uri)) {
// // If we are not inside a hidden iframe, try authenticating silently.
// await AuthenticationService.instance.trySilentSignIn();
// }
const user = await this._userManager.getUser();
return user && user.profile;
}
async getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult> {
const user = await this._userManager.getUser();
if (hasValidAccessToken(user) && hasAllScopes(request, user.scopes)) {
return {
status: AccessTokenResultStatus.Success,
token: {
grantedScopes: user.scopes,
expires: getExpiration(user.expires_in),
value: user.access_token
}
};
} else {
try {
const parameters = request && request.scopes ?
{ scope: request.scopes.join(' ') } : undefined;
const newUser = await this._userManager.signinSilent(parameters);
return {
status: AccessTokenResultStatus.Success,
token: {
grantedScopes: newUser.scopes,
expires: getExpiration(newUser.expires_in),
value: newUser.access_token
}
};
} catch (e) {
return {
status: AccessTokenResultStatus.RequiresRedirect
};
}
}
function hasValidAccessToken(user: User | null): user is User {
return !!(user && user.access_token && !user.expired && user.scopes);
}
function getExpiration(expiresIn: number) {
const now = new Date();
now.setTime(now.getTime() + expiresIn * 1000);
return now;
}
function hasAllScopes(request: AccessTokenRequestOptions | undefined, currentScopes: string[]) {
const set = new Set(currentScopes);
if (request && request.scopes) {
for (const current of request.scopes) {
if (!set.has(current)) {
return false;
}
}
}
return true;
}
}
async signIn(state: unknown) {
try {
await this._userManager.clearStaleState();
await this._userManager.signinRedirect(this.createArguments(state));
return this.redirect();
} catch (redirectError) {
return this.error(this.getExceptionMessage(redirectError));
}
// try {
// await this._userManager.clearStaleState();
// await this._userManager.signinSilent(this.createArguments());
// return this.success(state);
// } catch (silentError) {
// try {
// await this._userManager.clearStaleState();
// await this._userManager.signinRedirect(this.createArguments(state));
// return this.redirect();
// } catch (redirectError) {
// return this.error(this.getExceptionMessage(redirectError));
// }
// }
}
async completeSignIn(url: string) {
const requiresLogin = await this.loginRequired(url);
const stateExists = await this.stateExists(url);
try {
const user = await this._userManager.signinCallback(url);
if (window.self !== window.top) {
return this.operationCompleted();
} else {
return this.success(user && user.state);
}
} catch (error) {
if (requiresLogin || window.self !== window.top || !stateExists) {
return this.operationCompleted();
}
return this.error('There was an error signing in.');
}
}
async signOut(state: unknown) {
try {
if (!(await this._userManager.metadataService.getEndSessionEndpoint())) {
await this._userManager.removeUser();
return this.success(state);
}
await this._userManager.signoutRedirect(this.createArguments(state));
return this.redirect();
} catch (redirectSignOutError) {
return this.error(this.getExceptionMessage(redirectSignOutError));
}
}
async completeSignOut(url: string) {
try {
if (await this.stateExists(url)) {
const response = await this._userManager.signoutCallback(url);
return this.success(response && response.state);
} else {
return this.operationCompleted();
}
} catch (error) {
return this.error(this.getExceptionMessage(error));
}
}
private getExceptionMessage(error: any) {
if (isOidcError(error)) {
return error.error_description;
} else if (isRegularError(error)) {
return error.message;
} else {
return error.toString();
}
function isOidcError(error: any): error is (Oidc.SigninResponse & Oidc.SignoutResponse) {
return error && error.error_description;
}
function isRegularError(error: any): error is Error {
return error && error.message;
}
}
private async stateExists(url: string) {
const stateParam = new URLSearchParams(new URL(url).search).get('state');
if (stateParam && this._userManager.settings.stateStore) {
return await this._userManager.settings.stateStore.get(stateParam);
} else {
return undefined;
}
}
private async loginRequired(url: string) {
const errorParameter = new URLSearchParams(new URL(url).search).get('error');
if (errorParameter && this._userManager.settings.stateStore) {
const error = await this._userManager.settings.stateStore.get(errorParameter);
return error === 'login_required';
} else {
return false;
}
}
private createArguments(state?: unknown) {
return { useReplaceToNavigate: true, data: state };
}
private error(message: string) {
return { status: AuthenticationResultStatus.Failure, errorMessage: message };
}
private success(state: unknown) {
return { status: AuthenticationResultStatus.Success, state };
}
private redirect() {
return { status: AuthenticationResultStatus.Redirect };
}
private operationCompleted() {
return { status: AuthenticationResultStatus.OperationCompleted };
}
}
export class AuthenticationService {
static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication';
static _initialized: Promise<void>;
static instance: OidcAuthorizeService;
static _pendingOperations: { [key: string]: Promise<AuthenticationResult> | undefined } = {}
public static init(settings: UserManagerSettings & AuthorizeServiceSettings) {
// Multiple initializations can start concurrently and we want to avoid that.
// In order to do so, we create an initialization promise and the first call to init
// tries to initialize the app and sets up a promise other calls can await on.
if (!AuthenticationService._initialized) {
AuthenticationService._initialized = AuthenticationService.initializeCore(settings);
}
return AuthenticationService._initialized;
}
public static handleCallback() {
return AuthenticationService.initializeCore();
}
private static async initializeCore(settings?: UserManagerSettings & AuthorizeServiceSettings) {
const finalSettings = settings || AuthenticationService.resolveCachedSettings();
if (!settings && finalSettings) {
const userManager = AuthenticationService.createUserManagerCore(finalSettings);
if (window.parent !== window && !window.opener && (window.frameElement && userManager.settings.redirect_uri &&
location.href.startsWith(userManager.settings.redirect_uri))) {
// If we are inside a hidden iframe, try completing the sign in early.
// This prevents loading the blazor app inside a hidden iframe, which speeds up the authentication operations
// and avoids wasting resources (CPU and memory from bootstrapping the Blazor app)
AuthenticationService.instance = new OidcAuthorizeService(userManager);
// This makes sure that if the blazor app has time to load inside the hidden iframe,
// it is not able to perform another auth operation until this operation has completed.
AuthenticationService._initialized = (async (): Promise<void> => {
await AuthenticationService.instance.completeSignIn(location.href);
return;
})();
}
} else if (settings) {
const userManager = await AuthenticationService.createUserManager(settings);
AuthenticationService.instance = new OidcAuthorizeService(userManager);
} else {
// HandleCallback gets called unconditionally, so we do nothing for normal paths.
// Cached settings are only used on handling the redirect_uri path and if the settings are not there
// the app will fallback to the default logic for handling the redirect.
}
}
private static resolveCachedSettings(): UserManagerSettings | undefined {
const cachedSettings = window.sessionStorage.getItem(`${AuthenticationService._infrastructureKey}.CachedAuthSettings`);
return cachedSettings ? JSON.parse(cachedSettings) : undefined;
}
public static getUser() {
return AuthenticationService.instance.getUser();
}
public static getAccessToken(options: AccessTokenRequestOptions) {
return AuthenticationService.instance.getAccessToken(options);
}
public static signIn(state: unknown) {
return AuthenticationService.instance.signIn(state);
}
public static async completeSignIn(url: string) {
let operation = this._pendingOperations[url];
if (!operation) {
operation = AuthenticationService.instance.completeSignIn(url);
await operation;
delete this._pendingOperations[url];
}
return operation;
}
public static signOut(state: unknown) {
return AuthenticationService.instance.signOut(state);
}
public static async completeSignOut(url: string) {
let operation = this._pendingOperations[url];
if (!operation) {
operation = AuthenticationService.instance.completeSignOut(url);
await operation;
delete this._pendingOperations[url];
}
return operation;
}
private static async createUserManager(settings: OidcAuthorizeServiceSettings): Promise<UserManager> {
let finalSettings: UserManagerSettings;
if (isApiAuthorizationSettings(settings)) {
const response = await fetch(settings.configurationEndpoint);
if (!response.ok) {
throw new Error(`Could not load settings from '${settings.configurationEndpoint}'`);
}
const downloadedSettings = await response.json();
finalSettings = downloadedSettings;
} else {
if (!settings.scope) {
settings.scope = settings.defaultScopes.join(' ');
}
if (settings.response_type === null) {
// If the response type is not set, it gets serialized as null. OIDC-client behaves differently than when the value is undefined, so we explicitly check for a null value and remove the property instead.
delete settings.response_type;
}
finalSettings = settings;
}
window.sessionStorage.setItem(`${AuthenticationService._infrastructureKey}.CachedAuthSettings`, JSON.stringify(finalSettings));
return AuthenticationService.createUserManagerCore(finalSettings);
}
private static createUserManagerCore(finalSettings: UserManagerSettings) {
const userManager = new UserManager(finalSettings);
userManager.events.addUserSignedOut(async () => {
userManager.removeUser();
});
return userManager;
}
}
declare global {
interface Window { AuthenticationService: AuthenticationService }
}
AuthenticationService.handleCallback();
window.AuthenticationService = AuthenticationService;
Then build the js with
yarn build:release
Once the js file is compiled, copy the AuthenticationService.js file into the/wwwroot directory of your Blazor WASM app.
Then in the index.html file, comment out the MS script and replace with your own:
<!-- <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>-->
<script src="AuthenticationService.js"></script>
Run your app and Cognito will now be (relatively) instantaneous
I ended up switching from Cognito to Auth0 and upgrading from Api Gateway's RestApi to HttpApi, which includes built in JWT authorizer, and I am very happy with the change. Cognito just had too many problems in the end, but if someone is determined to get it to work check #aguafrommars's comments under the accepted answer.
Response 1:
While the authorizing message is displayed, the app check for a valid authentication and set up auto renew token iframe. If you look at the network log on your browser you'll see requests made by this time.
When the app run in release it's faster.
Response 2:
You need to add authorization on pages you want to protect by adding the Authorize attribute.
#page "/"
#attribute [Authorize]
Had the same problem and switched out to Azure B2C which again resolved the issue. Appears to be a problem with the auth library when linking to AWS Cognito as the auth provider.
Issues raised with MS -
https://github.com/dotnet/aspnetcore/issues/22651

I can't get pushToken in Safari

In my work, we tried integrate Safari Push unsuccessful. We have the files .p12 and the .cer
When executing in Safari, not work, no response. If change the name of callback, for example for console.log('hi')... print "hi" and in the console, I see the error
"TypeError: undefined in not an object (evaluating
'window.safari.pushNotification.requestPErmission('https://fabse.tv,
pushId, { user: '123456'}, console.log('hi'))')"
This is my code:
my pushId: 'web.fbase.tv'
setupPushWeb() {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var checkRemotePermission = function (permissionData) {
if (permissionData.permission === 'default') {
// This is a new web service URL and its validity is unknown.
window.safari.pushNotification.requestPermission(
'https://fbase.tv', // The web service URL.
'web.fbase.tv', // The Website Push ID.
{user_id: '4741619481'}, // Data used to help you identify the user.
console.log('hola') // The callback function.
);
}
else if (permissionData.permission === 'denied') {
// The user said no.
}
else if (permissionData.permission === 'granted') {
// The web service URL is a valid push provider, and the user said yes.
// permissionData.deviceToken is now available to use.
}
}
if(isSafari) {
if ('safari' in window && 'pushNotification' in window.safari) {
var permissionData = window.safari.pushNotification.permission('web.fbase.tv');
checkRemotePermission(permissionData);
}
} else {
//Firebase integration Fine

Execute custom functions one after another - Callback logic in Vue.js

There is a form which submits some data to an API in my component. Assume that it's method is ProcessLogin(). Inside this function I have written my API calls using axios. With the help of then() I have handled my server response and displayed my toast. All good.
Now as a part of my code clean up, I have decided to move all my axios functions to another api.js file and export functions from there. Here is an example function I have in my api.js file :
function ApiLogin(data) {
const url = `${BASE_URL}/authenticate`;
axios.post(url,data).then(response => {
return response;
}).catch(error => {
return error.response;
});
}
On the other side in my component I have my method defined as below :
methods: {
ProcessLogin() {
var status = ApiLogin(this.data);
console.log(status);
}
}
When executing this, I get undefined on my console. I know why it is happening. Because console.log(status) executes before ApiLogin could process and sends it's response. How to handle this kind of situation.? I know that callback is the rescue here, but I am not really sure about how to integrate it.
If you return the axios call from your ApiLogin function:
function ApiLogin(data) {
const url = `${BASE_URL}/authenticate`
return axios.post(url, data)
}
You could then handle the response in your component using then and console log from there:
methods: {
ProcessLogin() {
ApiLogin(this.data)
.then(res => console.log(res))
.catch(err => console.log(err))
}
}
...or with async/await:
methods: {
ProcessLogin: async function() {
try {
var status = await ApiLogin(this.data)
console.log(status)
}
catch(err) {
console.log(err)
}
}
}