Ember-Simple-Auth: How to inject token in ajax request - javascript

sorry for that (maybe) silly question. Im super new to this topic!
I created a custom authorizer:
import Ember from 'ember';
import Base from 'ember-simple-auth/authorizers/base';
export default Base.extend({
authorize: function(jqXHR, requestOptions) {
var accessToken = this.get('session.content.secure.token');
if (this.get('session.isAuthenticated') && !Ember.isEmpty(accessToken)) {
jqXHR.setRequestHeader('Authorization', 'Bearer ' + accessToken);
}
}
});
And now i want to include the token in a ajax request in my controller (this is my code without the token send):
// app/controllers/workouts.js
import Ember from 'ember';
import config from '../config/environment';
export default Ember.Controller.extend({
requestEndpoint: config.ServerIp+'/workouts',
workouts: function() {
Ember.$.ajax({
type: "GET",
url: requestEndpoint
}).success(function(data) {
return data;
})
}.property()
});
Thank you very much for helping and understanding this great module!

You could have something like this.
In your authorizer:
// app/authorizers/your-authorizer.js
import BaseAuthorizer from 'ember-simple-auth/authorizers/base';
export default BaseAuthorizer.extend({
authorize(data, block) {
const accessToken = data.accessToken; //Data is the response returned by the server
if (!Ember.isEmpty(accessToken)) {
block('Authorization', `Bearer ${accessToken}`);
}
}
});
The adapter will take care of adding the authorization header to all your requests:
// app/adapters/application.js
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
authorizer: 'authorizer:your-authorizer'
});
If you are not using ember data, you can take a look the way this mixin works to create your own adapter: data-adapter-mixin
To protect your route from access if the users are not logged, you need to add the authenticated mixin:
// app/routes/home.js
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Route.extend(AuthenticatedRouteMixin, {
...
});
And don't forget to set some configuration
// config/environment.js
...
var ENV = {
...
'ember-simple-auth': {
authenticationRoute: 'login',
routeAfterAuthentication: 'home',
routeIfAlreadyAuthenticated: 'home'
}
}

Related

Having issues updating legacy Angular project use old HTTP to new HTTPClient for basic auth

This is in an Angular 7 project and the code was written maybe 5-6 years ago, and I'm trying to update the application to latest Angular. Right now I'm just testing the login portion of the app which is basic http authentication. This is the last piece I need to refactor but im pretty lost. I don't want to break the entire system or do a full revamp just get something working so the whole ui can be upgraded from angular 7 to 14.
This is the old code below.
import { ConnectionBackend, Http, Request, RequestOptions, RequestOptionsArgs, Response, XHRBackend } from '#angular/http';
import { AuthService } from './../auth/auth.service';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
#Injectable()
export class AuthHttp extends Http {
constructor(backend: XHRBackend, defaultOptions: RequestOptions, private auth : AuthService) {
super(backend, defaultOptions);
this.auth = auth;
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
// hack to deal with compatibility issue of rxjs versions
// see https://stackoverflow.com/questions/38168581/observablet-is-not-a-class-derived-from-observablet
let optionsAny = <any>options;
let opts: RequestOptionsArgs = {
method: optionsAny.method,
body: optionsAny.body,
withCredentials: true,
headers: optionsAny.headers,
}
let query = '';
for (let k of Array.from(optionsAny.params.paramsMap.keys())) {
let v = optionsAny.params.paramsMap.get(k);
query += '&' + k + '=' + encodeURIComponent(v);
}
if (query) {
url = url + '?' + query.substring(1);
}
return Observable.create(sub => {
super.request(url, opts).subscribe(
res => { sub.next(res); },
err => {
if (err.status === 401) {
this.auth.signout();
} else {
sub.error(err);
}
} );
});
}
}
I tried some stuff but it all seems to lead to no where. I'm not really sure where to start. This is my first time using angular and it's a decently sized project. I feel like there's a really simple way to do this, but I'm not totally sure, I've been researching and haven't found anyone quite doing anything like this.
It compiles with no errors with an empty class. Like below, these imports are all the equivalent or roughly equivalent for the new angular httpclientmodule I believe .
import { AuthService } from './../auth/auth.service';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpParams, HttpHeaders, HttpXhrBackend, HttpResponse, HttpBackend, HttpRequest, HttpEvent } from '#angular/common/http';
#Injectable()
export class AuthHttp extends HttpClient {}
Then I get 2 errors below in the console of my browser when i do ng serve with the blank class above.
ERROR NullInjectorError: R3InjectorError(AppModule)[ApiService -> ApiService -> ApiService]:
NullInjectorError: No provider for ApiService!
Angular 9
AppComponent_Factory app.component.ts:11
Angular 26
631 main.ts:11
Webpack 7
core.mjs:6362:22
Angular 16
Any help on where to start or helpful resource is appreciated thank you.
This is how ApiService is implemented
#Injectable()
export class ApiService {
private apiUrl: string;
constructor(private authHttp: AuthHttp, private auth: AuthService) {}
getUrl() {
return this.apiUrl;
}
setUrl(url: string) {
this.apiUrl = url;
}
getConfiguration(): Configuration {
const token = this.auth.getToken();
return new Configuration({
accessToken: token
});
}
getUserApi(): UserService {
return new UserService(this.authHttp, this.apiUrl, this.getConfiguration());
}
getProductionApi(): ProductionService {
return new ProductionService(
this.authHttp,
this.apiUrl,
this.getConfiguration()
);
}
Without seeing the implementation of ApiService and its difficult to identify the issue. Broadly speaking it seems to me like the ApiService isn't provided from any module.
A quick check would be to provide it to the root of the application.
#Injectable({
providedIn: 'root',
})
export class ApiService ....
Ok I got it working but I'm still not totally sure how it's working or what it's doing. Basically the old method was doing this in a complicated why because of the limitations of older angular 2.
#Injectable()
export class AuthHttp extends HttpClient implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({
withCredentials: true
});
return next.handle(authReq);
}
}

Cannot find name 'ENV' returnTo: ENV.BASE_URI

I assume my authentication service fails to load a configuration file causing an error.
Im following along a tutorial on AUTH0.Here is the link https://auth0.com/blog/real-world-angular-series-part-2/.
I have to import a file from env.config.ts that will be used by my authentication service.Here is the code env.config.ts
const _isDev = window.location.port.indexOf('4200') > -1;
const getHost = () => {
const protocol = window.location.protocol;
const host = window.location.host;
return `${protocol}//${host}`;
};
const apiURI = _isDev ? 'http://localhost:8083/api/' : `/api/`;
export const ENV = {
BASE_URI: getHost(),
BASE_API: apiURI
};
Here is a quote from the tutorial "This code detects the host environment and sets the app's base URI and base API URI. We'll import this ENV configuration wherever we need to detect and use these URIs."
This env file is imported into a file required by auth0,
Here is my snippet of the auth.config.ts
import { ENV } from './../core/env.config';
interface AuthConfig {
CLIENT_ID: string;
CLIENT_DOMAIN: string;
AUDIENCE: string;
REDIRECT: string;
SCOPE: string;
NAMESPACE: string;
};
export const AUTH_CONFIG: AuthConfig = {
CLIENT_ID: '[xxx]',
CLIENT_DOMAIN: '[]', // e.g., kmaida.auth0.com
AUDIENCE: '[http://localhost:8083/api/]', // e.g., http://localhost:8083/api/
REDIRECT: `${ENV.BASE_URI}/callback`,
SCOPE: 'openid profile email',
NAMESPACE: 'http://myapp.com/roles'
};
Please have a look at the tutorial and maybe share what i could have missed.
The auth.service.ts uses the configutaion file as such
// Remove data from localStorage
this._clearExpiration();
this._clearRedirect();
// End Auth0 authentication session
this._auth0.logout({
clientId: AUTH_CONFIG.CLIENT_ID,
returnTo: ENV.BASE_URI
});
}
this is what my imports section looks like of auth.service.ts
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { BehaviorSubject, Subscription, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { AUTH_CONFIG } from './auth.config';
import * as auth0 from 'auth0-js';
import { ENV } from './../core/env.config';
The error emmited in my cmd reads
ERROR in src/app/auth/auth.service.ts:92:17 - error TS2304: Cannot find name >'ENV'.
92 returnTo: ENV.BASE_URI.
I also suspect the way the imports are handled from core have a problem ,as i really im not well vast with the slash imports.
I have my app directory that holds the auth and core folder as direct children.
try declaring ENV inside a constructor.
public constructor(){
const env = ENV
}
Then use it.
console.log(env.BASE_URI)

setting rootURL causing 404 error on page refresh

If I set language as rootURL this works I am able to go to any route/subroute and current language will be present in url, but on page refresh browser is trying get app from that language folder any thoughts? :/ I am using Ember 1.11.
// router.coffee
`import Ember from 'ember';`
`import config from './config/environment';`
Router = Ember.Router.extend
location: config.locationType
rootURL: '/' + localStorage.getItem('locale') + '/'
// config/environment.js
module.exports = function(environment) {
var ENV = {
locationType: 'history',
baseURL: '/'
...
You shouldn't use rootURL for this purpose. Instead, create a route that will be a parent to all other routes:
//../app/router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend(
{
location: config.locationType
}
);
Router.map(
function ()
{
this.route(
'lang', { path: '/:lang' }, function ()
{
this.route('index', { path: '/' });
this.route('404', { path: '/*wildcard' });
this.route('your-route-name');
}
);
}
);
export default Router;
Than you can use afterModel method of lang route to determine desired locale:
//../app/routes/lang.js
import config from '../config/environment';
export default Ember.Route.extend(
{
afterModel: function (params)
{
var allowedLocales = config.i18n.allowedLocales;
var defaultLocale = config.i18n.defaultLocale;
this.set(
'i18n.locale',
params && params.lang && allowedLocales.indexOf(params.lang) > -1 ? params.lang : defaultLocale
);
}
}
);
And in index route you need to detect user's locale from browser's settings or use default one:
//../app/routes/index.js
import config from '../config/environment';
export default Ember.Route.extend(
{
beforeModel: function ()
{
var allowedLanguages = config.i18n.allowedLocales;
var language = config.i18n.defaultLocale;
if (navigator.languages) {
for (let lang of navigator.languages) {
if (allowedLanguages.indexOf(lang) > -1) {
language = lang;
break;
}
}
} else {
if (navigator.language) {
language = navigator.language;
} else {
if (navigator.userLanguage) {
language = navigator.userLanguage;
}
}
}
this.transitionTo('lang.index', { lang: language });
}
}
);
BTW, your Ember version is quite old. You may want to upgrade it to 1.13 (1.13.x shouldn't break your app, 2.x could).

In Vue and Vue Router SPA, showing 404 for a not found resource

I'm using Vue and Vue Router in a SPA. In a view component I query a repository for a resource. If the resource is not found I want to show a 404 page whilst keeping the URL.
I.e. if I visit /foo/non-existant-id then a 404 page should be shown in place of the show page for the foo resource.
For clarity here is my router map:
router.map({
'/foo/:id': {name: 'foo-show', component: FooShowPage},
// Utilities
'/': { name: 'home', component: HomePage },
'*': { name: '404', component: NotFoundPage }
})
And in my FooShowPage I do the following:
ready () {
// fetch the foo from the repo (app.foos)
app.foos.fetchById(this.$route.params.id).then(foo => {
this.foo = foo
}).catch(e => {
// foo is not found show a 404 page
// using this.$route.router.go({name: '404'}) does not work as route is a wildcard
console.warn(e)
})
}
Essentially it would probably involve replacing the FooShowPage in the router view with NotFoundPage, or redirecting to a defined 404 page whilst keeping the browser history untouched.
You need to set a route for 404 page and then redirect unmatched routes to it. I use a router.redirect after the map to do such things.
router.map({
'/': { name: 'home', component: HomePage },
'/foo/:id': {name: 'foo-show', component: FooShowPage},
'/404': {name: 'not-found', component: NotFound}
})
router.redirect({
'*': '/404'
})
All routes that are NOT listed in the map will then be redirected to /404
Found a solution at Vue.js forum — use navigation guard:
import store from '../store'
{
path: '/lavori/:lavoro',
name: 'lavoro',
component: Lavoro,
beforeEnter: (to, from, next) => {
function isValid (id) {
return store.getters.resourceByID(id) !== undefined
}
if (!isValid(to.params.id)) {
next({ name: 'not-found' });
}
next();
}
},
Edit1: need to import store to get access to getters, from this Github issue and this question
Still a question how to leave same (requested) URL
The best I've figured out how to do is to use a global interceptor with Axios to redirect all 404 responses received through the API the 404 route. However that does change the url to /404 like #Leo's answer.
const http = axios.create({
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
// Add some global response intercepters
http.interceptors.response.use(function (response) {
// For successes just continue as normal
return response;
}, function (error) {
// If we have a 404 redirect to the error page replacing the history
if (error.response.status === 404) {
return router.replace({ name: 'notfound' });
}
return Promise.reject(error);
});
export default http;

How to make Ember Cli Mirage to work with Ember Simple auth

For development and testing I want to use Ember CLi Mirage. I'm trying to get it to work with simple auth and oauth2. How do I have to set up Mirage to work with a session token?
This is what I'm doing so far:
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
authenticate() {
var data = this.getProperties('username', 'password');
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data);
}
}
});
And in mirage I'm not sure how to set up my token route:
this.post('/token');
For custom work like this, pass a function in as the second parameter to your route definition:
this.post('/token', function(db, request) {
// generate a token
return {
token: token
};
});
I'd have to know more about your backend to offer more specific guidance, but this is the general idea. Hope it helps!
I use the following in my tests:
import { test } from 'qunit';
import { authenticateSession } from 'app-name/tests/helpers/ember-simple-auth';
import moduleForAcceptance from 'app-name/tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | whatever');
test('visiting /subpage-that-requires-authentication', function(assert) {
authenticateSession(this.application);
visit('subpage-that-requires-authentication');
andThen(function() {
assert.equal(currentURL(), 'subpage-that-requires-authentication');
});
});

Categories