I'm currently developing an Ionic (3.4) app for iOS/Android.
I'm not familiar with authentification processes or with applications.
After using HTTP request in a provider, I get my object, the user. But I have absolutely no idea how to store it or to deal with it. What are the good practices:
- Am I suppose to just store the object in the provider and call in in every single page I have?
- Am I suppose to store it as a sort of global object in the app?
- Or am I suppose (as bellow) to transfer it as a Navigation variable from one page to another? (I guess that's not the right answer)
And finally, how will the application react on reloading/restarting: how to avoid people to fill the form again and again?
Here is my service:
@Injectable()
export class UserProvider {
data = {};
username: string;
password: string;
constructor(private _http: Http) {
console.log('Hello UserProvider Provider');
}
getUser(username:string, password:string){
let urlSearchParams = new URLSearchParams();
urlSearchParams.append('username', username);
urlSearchParams.append('password', password);
let params = urlSearchParams.toString()
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
return this._http
.post("http://api.minesdedouai.fr/user/login", params, {headers:headers} )
.map(response => response.json())
}
}
I'm aware that a login request is supposed to use some token, but as I said, this is a small student's API, we're not pro and it's working.
And this is the method that uses the service, somewhere in a component (a page).
onLogin(form: NgForm) {
if (form.valid) {
this._userProvider.getUser(this.login.username, this.login.password)
.subscribe((response) => {
this.user = response;
console.log("Authentification Success");
this.navCtrl.push(TabsPage, {user: this.user});
},
(error) => {
if (error.status===403){
console.log("Wrong User")
}
else{
console.log(error);
}
});
}
}
Thank you very much
I will expose you how I deal with authentification on my ionic app.
The first step consists of creating a representation of the data you want to store. I used to store some user's information and a jwt token for the authentification.
user.ts
:
export class AuthenticatedUser {
private _username: string;
private _id: number;
private _token: string;
public static GetNewInstance(): AuthenticatedUser {
return new AuthenticatedUser(null, null, null)
}
public static ParseFromObject(object): AuthenticatedUser {
const model = AuthenticatedUser.GetNewInstance();
if (object) {
model.username = object.username;
model.id = object.id;
model.token = object.token;
}
return model;
}
constructor(username: string, id: number, token: string) {
this._username = username;
this._id = id;
this._token = token;
}
get username(): string {
return this._username;
}
set username(value: string) {
this._username = value;
}
get id(): number {
return this._id;
}
set id(value: number) {
this._id = value;
}
get token(): string {
return this._token;
}
set token(value: string) {
this._token = value;
}
}
- The
GetNewInstance
method is used to return an instantiated AuthenticatedUser
variable.
- The
ParseFromObject
method is used to return an AuthenticatedUser
from an object. It's very useful to convert an api response object
into a model.
After that I create some services. The first one is the UserService. I use it to access my AuthenticatedUser
inside my local storage. In the local storage the data are persistence and will not be delete when you close the app.
user.service.ts
:
export class UsersService {
private _user: Subject<AuthenticatedUser> = new Subject<AuthenticatedUser>();
constructor(private storage: Storage) {}
/* ---------------------------------------------------------------------------------------------------------------- */
/* Observable use object */
public subscribeToUserService(callback) {
return this._user.subscribe(callback);
}
public updateUserService(user: AuthenticatedUser) {
this._user.next(user);
}
/* ---------------------------------------------------------------------------------------------------------------- */
/* User storage management */
/**
* Write user properties in the local storage.
*
* @param user
* @returns {Promise<AuthenticatedUser>}
*/
createOnStorage(user: AuthenticatedUser): Promise<AuthenticatedUser> {
return new Promise((resolve) => {
this.getOnStorage().then((res) => {
if (res) {
this.deleteOnStorage().then(() => {
});
}
}).then(() => {
this.updateUserService(user);
this.storage.set('user', JSON.stringify(user));
resolve();
});
});
}
/**
* Get user properties from local storage.
*
* @returns {Promise<AuthenticatedUser>}
*/
getOnStorage(): Promise<AuthenticatedUser> {
return new Promise((resolve) => {
this.updateUserService(JSON.parse(this.storage.get('user')));
resolve(this.storage.get('user'));
});
}
/**
* Get user properties from local storage.
*
* @returns {Promise<AuthenticatedUser>}
*/
getOnStorageSync() {
this.updateUserService(JSON.parse(this.storage.get('user')));
return this.storage.get('user');
}
/**
* Update user properties from local storage.
*
* @param user
* @returns {Promise<AuthenticatedUser>}
*/
updateOnStorage(user: AuthenticatedUser): Promise<AuthenticatedUser> {
return new Promise((resolve) => {
resolve(this.storage.get('user'));
});
}
/**
* Delete user properties from local storage.
*
* @returns {Promise<AuthenticatedUser>}
*/
deleteOnStorage(): Promise<AuthenticatedUser> {
return new Promise((resolve) => {
this.storage.clear();
resolve();
});
}
}
The second service is ApiService
. This service is use to send different HTTP
methods and handled answers. In this service we will Format the header and access to the authenticatedUser's token.
@Injectable()
export class ApiService {
private baseUrl = "https://my.api.co";
private user: AuthenticatedUser;
/* ---------------------------------------------------------------------------------------------------------------- */
constructor(private userService: UserService, private http: Http) {
getAuthUser()
}
private getAuthUser() {
this.userService.getOnStorage.then(
(user) => {
this.user = user;
});
}
/**
* Get the Json Web Token from the local storage.
*
* @returns {RequestOptions}
*/
private formatHeader(): RequestOptions {
const headers: Headers = new Headers();
if (this.user.token) {
headers.append('Authorization', 'Bearer ' + this.user.token);
}
return new RequestOptions({headers});
}
/**
* Get the body of an HTTP response.
*
* @param res
* @returns {any|{}}
*/
private handleBody(res: Response) {
return res.json() || {};
}
/**
* Format the error message of an HTTP response.
*
* @param error
* @returns {any}
*/
private handleError(error: Response | any) {
let errorModel: any = {};
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errorModel = { status: error.status, message: `${error.status} - ${err.cause} ` };
} else {
errorModel = { status: error.status, message: error.toString()};
}
return Observable.throw(errorModel);
}
/* ---------------------------------------------------------------------------------------------------------------- */
/**
* Perform a PUT request.
*
* @param url
* @param auth
* @param body
* @returns {Observable<>}
*/
putRequest(url: string, body: Object, auth: boolean = true): Observable<Object> {
let header = null;
if (auth) {
header = ApiService.formatHeader();
}
return this.http.put(this.BASE_URL + url, body, header)
.map(ApiService.handleBody)
.catch(ApiService.handleError);
}
/**
* Perform a POST request.
*
* @param url
* @param auth
* @param body
* @returns {Observable<>}
*/
postRequest(url: string, body: Object, auth: boolean = true): Observable<Object> {
let header = null;
if (auth) {
header = ApiService.formatHeader();
}
return this.http.post(this.BASE_URL + url, body, header)
.map(ApiService.handleBody)
.catch(ApiService.handleError);
}
/**
* Perform a HEAD request.
*
* @param url
* @param auth
* @returns {Observable<>}
*/
headRequest(url: string, auth: boolean = true): Observable<Object> {
let header = null;
if (auth) {
header = ApiService.formatHeader();
}
return this.http.head(this.BASE_URL + url, header)
.map(ApiService.handleBody)
.catch(ApiService.handleError);
}
/**
* Perform a GET request.
*
* @param url
* @param auth
* @returns {Promise<>}
*/
getRequest(url: string, auth: boolean = true): Observable<Object> {
let header = null
if(auth) {
header = ApiService.formatHeader();
}
return this.http.get(this.BASE_URL + url, header)
.map(ApiService.handleBody)
.catch(ApiService.handleError);
}
/**
* Perform a DELETE request.
*
* @param url
* @param auth
* @returns {Observable<>}
*/
deleteRequest(url: string, auth: boolean = true): Observable<Object> {
let header = null;
if (auth) {
header = ApiService.formatHeader();
}
return this.http.delete(this.BASE_URL + url, header)
.map(ApiService.handleBody)
.catch(ApiService.handleError);
}
And the last service is the AuthService
. This service will send login/logout requests.
auth.service.ts
:
@Injectable()
export class AuthService {
private user: AuthenticatedUser;
/* ---------------------------------------------------------------------------------------------------------------- */
constructor(private userService: userService, private apiService: ApiService) {
this.getAuthUser();
}
getAuthUser() {
this.userService.getOnStorage().then(
(user) => {
this.user = user;
}
);
}
/* ---------------------------------------------------------------------------------------------------------------- */
/**
* Request an authentication access.
*
* @param email the email of the user
* @param password the password of the user
* @returns {Promise<any>}
*/
login(email: string, password: string): Promise<AuthentificatedUser> {
return new Promise((resolve, reject) => {
this.apiService.postRequest('/auth', {email: email, password: password})
.subscribe(
res => resolve(AuthentificatedUser.ParseFromObject(res)),
error => reject(<any>error));
});
}
/**
* Logout a user from the authentication process.
*
* @returns {Promise<any>}
*/
logout(): Promise<any> {
return new Promise((resolve) => {
this.userService.deleteOnStorage().then(() => {
resolve();
});
});
}
/**
* Check whether a user is already logged in.
*
* @returns {boolean}
*/
isLoggedIn() {
if (this.user.token) {
return true;
} else {
return false;
}
}
/* ---------------------------------------------------------------------------------------------------------------- */
}
In your login page :
this.authService.login('login', 'password').then(
(user) => {
this.userSerivce.createOnStorage(user);
this.navCtrl.push(HomePage);
}).catch(
(err) => {
//show the error
}
);
The last thing you have to do this to check if a user is connected or redirect the user to the login screen.
export HomePage() {
private user: AuthenticatedUser;
constructor(private userService: UserService) {
this.getAuthUser();
}
private getAuthUser() {
this.userService.getOnStorage().then(
(user) => {
this.user = user;
if (!this.user.token) {
this.navCtrl.push(LoginPage)
}
}
);
}
}
If you want to persist the data durably, you can use ionic-storage : http://ionicframework.com/docs/storage/