POST data ionic 3 and backend API restful laravel - rest

I have an issue about POST data at add student page. I'm using ionic 3 and backend API restful laravel, I tried this POST in postman and it's works, but I got an error message "invalid token or token not provided" after click Add button (refer the first picture). I'm not sure how to write method with auth token.
You can refer my provider (second image) ,addstudent.ts (third image), button html (forth image) for reference
Thanks in advance.

Suppose you stored your token in a storage (e.g localStorage or sessionStorage), you can do the following:
Step1: Add Auth provider and extend BaseRequestOptions
import { BaseRequestOptions, RequestOptions, RequestOptionsArgs } from '#angular/http';
import { Injectable } from '#angular/core';
#Injectable()
export class AuthProvider extends BaseRequestOptions {
constructor() {
super();
this.headers.set('Content-Type', 'application/json');
}
merge(options?: RequestOptionsArgs): RequestOptions {
let newOptions = super.merge(options);
let accessToken = localStorage.getItem('accessToken');
if (accessToken !== null && accessToken !== undefined) {
let token: AcessTokenResponse = JSON.parse(accessToken);
newOptions.headers.set('Authorization', token.token_type + ' ' + token.access_token);
}
return newOptions;
}
}
Step2: Add AuthProvider under providers in app.module.ts file

As per my exprience you need to add RequestOptions in your POST call like below:
let options = new RequestOptions({
headers: headers
});
this.http.post(this.apiUrl+'/addstudent', addStudent, options)
As in your code you are creating Header but not passing it into post request.
Try above code Hope this will help you to get your API work.

Related

How to retrieve auth0 access token inside Axios Config file?

hope you’re all well and safe!
I'm currently working on a Vue 3 application with Pinia as my store, Auth0 Vue SDK for authentication/authorization and Axios to call my backend API.
In Auth0 docs, they recommend an access token be retrieved using the getAccessTokenSilently() method everytime I want to call my backend API:
const { getAccessTokenSilently } = useAuth0();
const accessToken = await getAccessTokenSilently();
The problem is I have to type this whenever I use axios in my component files to call my backend API with the access token.
Since I have too many endpoints, my plan is to pass the access token once in an axios request interceptor and use Pinia actions to call my APIs.
I’ve created a /config/axios.js file in my application that contains the following:
//Import Axios Library and Auth0
import axios from 'axios';
import { useAuth0 } from "#auth0/auth0-vue"
//Create instance of axios
const instance = axios.create({
baseURL: 'http://localhost:5000/api/v1'
});
// Create a request interceptor for my instance and get accessToken on the fly
instance.interceptors.request.use(async (config) => {
const { getAccessTokenSilently } = useAuth0();
const accessToken = await getAccessTokenSilently();
config.headers['Authorization'] = accessToken;
return config;
}, (error) => {
return Promise.reject(error)
});
export default instance;
Simple enough, just create a baseURL and intercept requests to add the authorization header with the access token.
Now in Pinia, I've created a user store that'll fetch users with the axios config as seen below:
//Import the Pinia Library
import { defineStore } from 'pinia'
//Import the Axios Library for API calls
import axiosConfig from "#/config/axios.js"
export const useUserStore = defineStore('user', {
state: () => ({
user:{}
}),
getters: {
getUser(state){
return state.user
}
},
actions: {
async fetchUser(){
try {
const data = await axiosConfig.get('/profile')
this.user = data.data
} catch (error) {
console.log("User Pinia error: " + error)
}
}
},
})
And lastly in my component file, I just import the store and call the Pinia action fetchUsers.
When trying an axios call, I get the following error!
TypeError: auth0_auth0_vue__WEBPACK_IMPORTED_MODULE_5_.useAuth0() is undefined
I can't figure out how to retrieve the access token from auth0 library in my interceptor function.
A similar question was raised as an issue on auth0-vue github:
https://github.com/auth0/auth0-vue/issues/99
The above link describes numerous approaches. I went for the plugin approach that is described in this PR, namely:
https://github.com/auth0-samples/auth0-vue-samples/commit/997f262dabbab355291e5710c51d8056a5b142cf
But the issue was officially resolved by offering a mechanism to allow the sdk from outside a vue component:
https://github.com/auth0/auth0-vue/issues/99#issuecomment-1099704276

Querying graphql from Ionic via Native Http

I'm migrating some services from Rest to Graphql in an Ionic App. I use the Ionic/Cordova native http plugin in order to make requests to my server, in the following way:
import {HTTP} from '#ionic-native/http/ngx'
#Injectable()
export class ApiProvider {
private server = "https://api.myserver.com/'
constructor(
private nativeHttp:HTTP
)
{}
getData(path:string,params = "")
{
const url = this.server + path + params
return this.nativeHttp.get(url,{},{
'Content-Type':'application/json'
})
}
}
My question is: How can I pass a graphql query in it? I've tried making the full url look like this https://api.myserver.com/graphql?query={people{id,name}} but I got the following error from graphql:
{
"errors": [
{
"message": "Unknown operation named \"IntrospectionQuery\"."
}
]
}
which differs from the successful result that I get in Postman for the exact same url.
How can I make this query using the http plugin?
Thank you in beforehand!

.Net Core: Validate Anti Forgery Token with Ionic front end

I have looked all over and have found similar solutions, but nothing that matches exactly what I'm working on.
We have a .net core MVC website with an API Controller for handling requests from an ionic mobile app which we are also developing.
In most cases, adding [ValidateAntiForgeryToken] to the API controller actions works. I have gone through the process of generating the token, passing it to Ionic, and storing it in the request headers for validation.
Here is the code I am using to fetch and store the token:
static XSRF_TOKEN_KEY: string = "X-XSRF-TOKEN";
static XSRF_TOKEN_NAME_KEY: string = "X-XSRF-TOKEN-NAME";
constructor(){}
static getXsrfToken(http: HTTP) : {tokenName: string, token: string} {
let tokenName: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);
let token: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
if(!tokenName || !token){
this.fetchXsrfToken(http);
tokenName= window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);
token = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
}
return {
tokenName: tokenName,
token: token
};
}
private static setXsrfToken({ token, tokenName }: { token: string, tokenName: string }) {
window.sessionStorage.setItem(ValidationManager.XSRF_TOKEN_KEY, token);
window.sessionStorage.setItem(ValidationManager.XSRF_TOKEN_NAME_KEY, tokenName);
}
private static fetchXsrfToken(http: HTTP) {
let token: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
let tokenName: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);
if (!token || !tokenName) {
let apiUrl: string = AppConfig.apiUrl + "/GetAntiforgeryToken";
http.get(apiUrl, {}, {})
.then(r => this.setXsrfToken(JSON.parse(r.data)))
.catch(r => console.error("Could not fetch XSRFTOKEN", r));
} else {
this.setXsrfToken({ token: token, tokenName: tokenName });
}
}
Here is the action in my controller that serves anti forgery tokens:
[HttpGet]
public override IActionResult GetAntiforgeryToken()
{
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
return new ObjectResult(new
{
token = tokens.RequestToken,
tokenName = tokens.HeaderName
});
}
I set the headers of the http plugin by calling this function from the view's associated typescript file:
initializeHttp() {
let token = ValidationManager.getXsrfToken(this.http);
this.http.setHeader(token.tokenName, token.token);
console.log("Http Initialized: ", token);
}
then any request I make with the http plugin is validated properly in the controller's action:
this.http.post(apiUrl, {}, {}).then(response => {
that.navCtrl.setRoot(HomePage);
});
Up to this point, everything works great. The problem arises when I try to use XmlHttpRequest to for a POST instead of the built-in http plugin:
let file = {
name: e.srcElement.files[0].name,
file: e.srcElement.files[0],
};
let formData: FormData = new FormData();
formData.append('file', file.file);
let xhr: XMLHttpRequest = new XMLHttpRequest();
xhr.open('POST', apiUrl, true);
console.log("setting request header: ", tokenVal); //verify that tokenVal is correct
xhr.setRequestHeader("X-XSRF-TOKEN", tokenVal);
xhr.send(formData);
If I remove the [ValidateAntiForgeryToken] attribute from the controller's action, the file is posted properly. However, nothing I have tried has worked with the attribute being included.
I believe the issue has something to do with the validation tokens being added to a cookie automatically by Ionic, and the cookie is passed along with the request from the http plugin. However, XMLHttpRequest does not pass the cookie along (and is unable to do so?).
I have read up on the subject quite a bit over the past few days but I admit that this validation is still mostly a black box to me. Is there a way to validate the request in my action using only the token which is passed up in the header?
The reason I am running into this problem is that I need to upload a file, which I was unable to do using the http plugin. There are solutions for uploading images using Ionic's file-transfer plugin, but it has been deprecated and the release notes suggest using XmlHttpRequest instead.
Other things I have tried:
I have found solutions for .net standard which use System.Web.Helpers.AntiForgery for custom validation on the server, but this namespace is not included in .net core and I could not find an equivalent.
I tried many different ways to post the file using the http plugin (since it has no issues validating the antiForgery token). Everything I tried resulted in the action being hit but the file being posted was always null. A solution which uploads a file using the http plugin would also be acceptable.
Why is it that I was able to spend two full days on this problem, but as soon as I post a question about it, I find the answer? Sometimes I think the internet gods are just messing with me.
As it turns out, the native http plugin has an uploadFile() function that I never saw mentioned anywhere else. Here's what the solution does:
Use the fileChooser plugin to select a file from the phone's storage
Use the filePath plugin to resolve the native filesystem path of the image.
Use http.uploadFile() instead of http.post()
This works because as mentioned above, I was able to properly set the validation token in the http plugin's header to be accepted by the controller.
And here is the code:
let apiUrl: string = AppConfig.apiUrl + "/UploadImage/";
this.fileChooser.open().then(
uri => {
this.filePath.resolveNativePath(uri).then(resolvedPath => {
loader.present();
this.http.uploadFile(apiUrl,{ },{ },resolvedPath, "image")
.then(result => {
loader.dismiss();
toastOptions.message = "File uploaded successfully!";
let toast = this.toastCtrl.create(toastOptions);
toast.present();
let json = JSON.parse(result.data);
this.event.imageUrl = json.imgUrl;
})
.catch(err => {
console.log("error: ", err);
loader.dismiss();
toastOptions.message = "Error uploading file";
let toast = this.toastCtrl.create(toastOptions);
toast.present();
});
});
}
).catch(
e => console.log(e)
);

SignatureDoesNotMatch error when uploading to s3 via a pre signed url using Ionic 2

I am trying to upload a video to s3 and have a pre-signed PUT url. The following is the code to do so.
import {Component} from '#angular/core';
import {NavController} from 'ionic-angular';
import {MediaCapture} from 'ionic-native';
import {Http} from '#angular/http';
import { Transfer } from 'ionic-native';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public base64Image: string;
constructor(private navController: NavController, public http: Http) {
this.base64Image = "https://placehold.it/150x150";
}
public takeVideo() {
MediaCapture.captureVideo({limit:2}).then(function(videoData){
var link = "https://mysamplebucket.s3.amazonaws.com/non-tp/esx.mov?AWSAccessKeyId=TEMP_KEYY&Expires=1482290587&Signature=JUIHHI%2FcnLkqSVg%3D&x-amz-security-token=FQoDYXDGRfTXk6hma0Rxew6yraAX%2FlYGaQmYLwkvsuuB3%2F%2FtPvGDVs3dIQG0Ty3MeMjn0p%%26djt5xhAMk73pndJbZP0tCYYlvPvlUAyL8x7O%%2B3AwEa%%2B9b43yarIuPLCvujmKLTDyi%%3D%3Di";
var options: any;
options = {
fileKey: 'file',
fileName: 'esx.mov',
httpMethod: 'PUT',
chunkedMode: false,
mimeType: 'video/quicktime',
encodeURI: false,
headers: {
'Content-Type': 'video/quicktime'
}
};
var ft = new Transfer();
ft.upload(videoData[0].fullPath, link, options, false)
.then((result: any) => {
this.success(result);
}).catch((error: any) => {
this.failed(error);
});
}, function(err){
alert(err);
});
}
}
Here is the code that generates the pre-signed PUT url.
var params = {Bucket: s3_bucket, Key: filename, Expires: 900000};
var url = {
'url' : s3.getSignedUrl('putObject', params)
};
I get, SignatureDoesNotMatch error. The message says, The request signature we calculated does not match the signature you provided. Check your key and signing method. I am not sure what I am doing wrong here - I looked a few other SO and Ionic questions and tried what they recommended to no avail. Any ideas on what I and doing wrong?
Your upload PUT request will have a Content-Type: video/quicktime header.
When the Content-Type header is present in the request (not the response), its value is a non-optional component in the Signature V2 canonical request... which means you have to pass it to the code generating the signature.
var params = {Bucket: s3_bucket, Key: filename, Expires: 900000}; also needs this string (video/quicktime, in this case) passed to it as ContentType: ... for a PUT request (but not for a GET request, since this describes the content you are sending, and GET requests customarily send no actual content.
The SDK documentation doesn't seem to specifically mention this, but it is most definitely required by S3.
In case someone else is looking at this and is in a similar situation as me, I got a similar SignatureDoesNotMatchError when my s3 bucket's CORS Configuration did not contain <AllowedHeader>*</AllowedHeader>
I ran into this when moving from one bucket to another, copying all the settings except for the CORS Configuration.
We faced this issue when we were downloading an already uploaded file. We were receiving the presigned url, but when tried to download the file with that presign url, it said "signature does not match".
The solution we received when we reported a ticket with AWS because all the approaches failed. The scenario is we have our custom AWS KMS encryption enabled for S3 bucket, but we were trying to send "kms key" along with our request when using GeneratePresignedUrlRequest api. AWS said, we don't have to send KMS key, instead send without encrypting from client. When I say unencrypted, it is not exactly that, it is already coming in encrypted form and when we were using "AWSS3V4SignerType" to sign, we were sending kms id as an additional param that wasn't required to begin with. Hope this makes sense.
The params AWS looks for in the header are:
Algorithm
Credential Scope
Signed headers
Date
Expiration Date
Signature
KMS Key - we were passing this, which wasn't required.

Ionic 2 - Storage always empty until reloading the app

I am currently building an application with Ionic 2 and using the Storage plugin to hold my values which are pretty much just an API Token and user profile since the application pulls all data from an API.
I am testing the application via ionic serve because no native functions are used but now I am facing the problem that every time I store a value in the Storage the value is not accessible until I reload the app which is kind of annoying because after the user logs in he gets redirected to a page that requires the API token which is not available until I reload the app so the whole thing gets stuck in a loop.
Ionic Storage is using IndexedDB in the browser where I can see that the values have been stored when I check them with Chrome Developer tools.
I have been trying to figure out the issue but can't find any reason why the storage values are not available until reloading the app.
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { HttpClientService } from './http-client-service';
import 'rxjs/add/operator/map';
#Injectable()
export class AuthService {
constructor(public events: Events, public storage: Storage, public http: HttpClientService) {
//
}
login(user) {
var response = this.http.post('login', {
email: user.email,
password: user.password,
});
response.subscribe(data => {
this.storage.set('api_token', data.token);
console.log('raw : ' + data.token); // shows the api token
this.storage.get('api_token').then((value) => {
console.log('storage : '+ value); // is empty...
});
});
return response;
};
}
Edit: I managed to track down the issue to the storage running async which results in the token not being added to the headers.
createAuthorizationHeader(headers: Headers) {
// this does add the header in time
localStorage.setItem('api_token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vYXBpLndpaHplLmRldi9sb2dpbiIsImlhdCI6MTQ4MTE4MzQyOCwiZXhwIjoxNDgxMTg3MDI4LCJuYmYiOjE0ODExODM0MjgsImp0aSI6IjdlNTE1WUEwWmE4NWc2QjUiLCJzdWIiOiIxIiwidXNlciI6eyJpZCI6MX19.T4KpqgCB8xU79vKyeLG4CJ0OHLpVI0j37JKIBJ_0CC4');
headers.append('Authorization', 'Bearer ' + localStorage.getItem('api_token'));
// this does not add the header in time
return this.storage.get('api_token').then((value) => {
headers.append('Authorization', 'Bearer ' + value);
});
}
getHeaders(path) {
let headers = new Headers();
headers.set('Accept', 'application/json');
headers.set('Content-Type', 'application/json');
if(!this.isGuestRoute(path)) {
this.createAuthorizationHeader(headers);
}
return new RequestOptions({ headers: headers });
}
get(path: string) {
return this._http.get(this.actionUrl + path, this.getHeaders(path))
.map(res => res.json())
.catch(this.handleError);
}
Alright, looked in the ionic docs and I do understand why you put them both underneath eachother since they also display it like that in the docs.
But Storage.set(key, value) :
Returns:
Promise that resolves when the value is set
This means that you cannot use it the way you are using it (hence why they added a comment with //or ....
Since resolving a Promise is asynchronous.
If you want to use the value like you're currently using it (which seems a bit odd but probably for you to test if the value is set correctly) you should use
this.storage.set('api_token', data.token).then(() => {
this.storage.get('api_token').then((value) => {
console.log('storage : '+ value); // is empty...
});
});
console.log('raw : ' + data.token); // shows the api token
If you would like some more information about why this happens, check out this SO answer (I prefer second one) Asynchronous vs synchronous execution, what does it really mean?