i have an angular basic project and a simple microservice jhipster project. since i chosed jwt option in my jhipster project , i want to consume methods from my angular project.after looking for hours i finnaly found this code which helped me to connect successfuly but i get a weird error which bloked me. here is my angular classes i used to try connect and consume jhipster methods:
auth-interceptor.ts file
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private token: TokenStorageService) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
let authReq = req;
const token = this.token.getToken();
if (token != null) {
authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
}
return next.handle(authReq);
}
}
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];
auth.service.ts
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: 'root'
})
export class AuthService {
private loginUrl = 'http://localhost:8082/api/authenticate';
private signupUrl = 'http://localhost:8080/api/auth/signup';
constructor(private http: HttpClient) {
}
attemptAuth(credentials: AuthLoginInfo): Observable<JwtResponse> {
return this.http.post<JwtResponse>(this.loginUrl, credentials, httpOptions);
}
signUp(info: SignUpInfo): Observable<string> {
return this.http.post<string>(this.signupUrl, info, httpOptions);
}
}
jwt-response.ts
export class JwtResponse {
accessToken: string;
type: string;
username: string;
authorities: string[];
}
token-storage.service.spec.ts
describe('TokenStorageService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TokenStorageService]
});
});
it('should be created', inject([TokenStorageService], (service: TokenStorageService) => {
expect(service).toBeTruthy();
}));
});
token.storage.service.ts
const TOKEN_KEY = 'AuthToken';
const USERNAME_KEY = 'AuthUsername';
const AUTHORITIES_KEY = 'AuthAuthorities';
#Injectable({
providedIn: 'root'
})
export class TokenStorageService {
private roles: Array<string> = [];
constructor() { }
signOut() {
window.sessionStorage.clear();
}
public saveToken(token: string) {
window.sessionStorage.removeItem(TOKEN_KEY);
window.sessionStorage.setItem(TOKEN_KEY, token);
}
public getToken(): string {
return sessionStorage.getItem(TOKEN_KEY);
}
public saveUsername(username: string) {
window.sessionStorage.removeItem(USERNAME_KEY);
window.sessionStorage.setItem(USERNAME_KEY, username);
}
public getUsername(): string {
return sessionStorage.getItem(USERNAME_KEY);
}
public saveAuthorities(authorities: string[]) {
window.sessionStorage.removeItem(AUTHORITIES_KEY);
window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
}
public getAuthorities(): string[] {
this.roles = [];
if (sessionStorage.getItem(TOKEN_KEY)) {
JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
this.roles.push(authority.authority);
});
}
return this.roles;
}
}
and finnaly this is my connection method:
onSubmit() {
console.log(this.form);
this.loginInfo = new AuthLoginInfo(
this.form.username,
this.form.password);
this.authService.attemptAuth(this.loginInfo).subscribe(
data => {
this.tokenStorage.saveToken(data.accessToken);
this.tokenStorage.saveUsername(data.username);
this.tokenStorage.saveAuthorities(data.authorities);
this.isLoginFailed = false;
this.isLoggedIn = true;
this.roles = this.tokenStorage.getAuthorities();
this.reloadPage();
},
error => {
console.log(error);
this.errorMessage = error.error.message;
this.isLoginFailed = true;
}
);
}
with this i am able to login successfully but once i login i get this error when i try to do anything else:
core.js:6014 ERROR SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at TokenStorageService.getAuthorities (token-storage.service.ts:45)
Guys if u know any simple method to connect to jhipster using jwt from angular please help me cause i am blocked here , i have a full jhipster project which i cant consume any..
Well, seems your server does not provide authorities in the JWT response. Since the data is missing, the saveAuthorities saves string "undefined" that cannot be served later.
If the reason authorities is missing is that you blindly copied some random code off the internet, delete the authorities from class JwtResponse and saveAuthorities, getAuthorities and all references to them.
Otherwise, just provide the authorities in your response.
Or you can check if authorities exist when saving them in onSubmit callback.
if (data.authorities)
this.tokenStorage.saveAuthorities(data.authorities);
You have a typo in your condition in the getAuthorities method. The token is undefined. The error you have comes from trying to parse undefined in JSON.parse(...). You check the TOKEN_KEY in storage but read AUTHORITIES_KEY
Your code:
if (sessionStorage.getItem(TOKEN_KEY)) {
JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
Correct code:
if (sessionStorage.getItem(AUTHORITIES_KEY)) {
JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
Related
I am implementing auth0 to my angular project,
and I get the, error loading discovery document in my auth.config service "this.oauthService.loadDiscoveryDocumentAndLogin()",
as the auth0 page was not found.
Please, any help is welcome. Thank you.
This is my component, here I declare variables that call my auth0 data that I have in the env
I have some doubts about the logout route,
reviewing forums I have seen that others do not put /v2
export const authConfig: AuthConfig = {
issuer: environment.keycloak.issuer,
redirectUri: environment.keycloak.redirectUri,
clientId: environment.keycloak.clientId,
responseType: environment.keycloak.responseType,
scope: environment.keycloak.scope,
requireHttps: environment.keycloak.requireHttps,
showDebugInformation: environment.keycloak.showDebugInformation,
disableAtHashCheck: environment.keycloak.disableAtHashCheck,
customQueryParams: {
audience: environment.audience
},
logoutUrl : `${environment.keycloak.issuer}v2/logout?client_id=${environment.keycloak.clientId}&returnTo=${encodeURIComponent(environment.keycloak.redirectUri)}`
}
export class OAuthModuleConfig {
resourceServer: OAuthResourceServerConfig = {sendAccessToken: false};
}
export class OAuthResourceServerConfig {
allowedUrls?: Array<string>;
sendAccessToken = true;
customUrlValidation?: (url: string) => boolean;
}
this is my service, I get the error in this.oauthService.loadDiscoveryDocumentAndLogin()
#Injectable()
export class AuthConfigService {
private _decodedAccessToken: any;
private _decodedIDToken: any;
get decodedAccessToken() { return this._decodedAccessToken; }
get decodedIDToken() { return this._decodedIDToken; }
constructor(
private readonly oauthService: OAuthService,
private readonly authConfig: AuthConfig) { }
async initAuth(): Promise<any> {
return new Promise((resolveFn, rejectFn) => {
// setup oauthService
this.oauthService.configure(this.authConfig);
this.oauthService.tokenValidationHandler = new NullValidationHandler();
// subscribe to token events
this.oauthService.events.pipe(filter((e: any) => {
if(e.params && e.params.error_description == 'user is blocked') {
console.log('Usuario bloqueado');
//Pagina de usuario bloqueado.
}
return e.type === 'token_received';
})).subscribe(() => this.handleNewToken());
// disabling keycloak for now
// resolveFn();
// continue initializing app or redirect to login-page
console.log(this.oauthService.loadDiscoveryDocumentAndLogin())
this.oauthService.loadDiscoveryDocumentAndLogin().then(isLoggedIn => {
console.log("isloggi",isLoggedIn)
if (isLoggedIn) {
this.oauthService.setupAutomaticSilentRefresh();
// #ts-ignore
resolveFn();
} else {
this.oauthService.initImplicitFlow();
rejectFn();
}
});
});
}
private handleNewToken() {
this._decodedAccessToken = this.oauthService.getAccessToken();
this._decodedIDToken = this.oauthService.getIdToken();
}
I'm trying to setup a prototype for using graphql across multiple java microservices, which requires me to join multiple graphql schema's into one.
I'm using 2 java-services and the ApolloServer with ApolloGateway; which shows the following schema in the playground:
type Client {
id: ID!
name: String
linkeduser: User
}
type Query {
user(id: ID!): User
users: [User]
client(id: ID!): Client
clients: [Client]
}
type User {
id: ID!
name: String
}
When running the simple query:
query client {
client(id: 1) {
id
name
linkeduser {
id
name
}
}
}
What I expect this to return is a client with a linkeduser; When debugging the client service gets queried, the user service gets queried, yet the response is:
{
"data": {
"client": {
"id": "1",
"name": "Bob",
"linkeduser": null
}
}
}
How do I get a linked user response in my client?
I've tried returning lists of users, a new client object with a list of linkedusers, a single user.
The example of https://github.com/apollographql/federation-jvm is the base of this code, though I've yet to see this working.
Code:
Service 1: Client
#WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet {
#Override
protected GraphQLConfiguration getConfiguration() {
return GraphQLConfiguration.with(getGraphQLSchema()).build();
}
private static GraphQLSchema getGraphQLSchema() {
InputStream inputStream = client.GraphQLService.class
.getClassLoader().getResourceAsStream("schema.graphqls");
TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getClient))
.build();
return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
.fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
.stream()
.map(values -> {
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return getSingleClient((String) id);
}
}
return null;
})
.collect(Collectors.toList()))
.resolveEntityType(env -> {
final Object src = env.getObject();
if (src instanceof Client) {
return env.getSchema().getObjectType("Client");
}
return null;
}).build();
}
private static Object getClient(DataFetchingEnvironment environment) {
switch (environment.getFieldDefinition().getName()) {
case "client":
return getSingleClient(environment.getArgument("id"));
case "clients":
return getAllClients();
default:
return null;
}
}
//... extra code with simple getters
}
With this schema :
extend type Query {
client(id: ID!): Client
clients: [Client]
}
type Client #key(fields: "id"){
id: ID!
name: String
}
Service 2: User
#WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet {
#Override
protected GraphQLConfiguration getConfiguration() {
return GraphQLConfiguration.with(getGraphQLSchema()).build();
}
private static GraphQLSchema getGraphQLSchema() {
InputStream inputStream = user.GraphQLService.class
.getClassLoader().getResourceAsStream("schema.graphqls");
TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getUser))
.build();
return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
.fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
.stream()
.map(values -> {
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return getSingleUser((String) id);
}
}
return null;
})
.collect(Collectors.toList()))
.resolveEntityType(env -> {
final Object src = env.getObject();
if (src instanceof User) {
return env.getSchema().getObjectType("User");
}
return null;
})
.build();
}
private static Object getUser(DataFetchingEnvironment environment) {
switch (environment.getFieldDefinition().getName()) {
case "user":
return getSingleUser(environment.getArgument("id"));
case "users":
return getAllUsers();
default:
return null;
}
}
//... extra code with simple getters
}
With this schema :
type Query #extends{
user (id: ID!): User
users: [User]
}
type User #key(fields: "id") {
id: ID!
name: String
}
type Client #key(fields: "id") #extends{
id: ID! #external
linkeduser : User
}
Version in POM.xml
<graphql.version>14.0</graphql.version>
<graphql-tools.version>5.2.4</graphql-tools.version>
<graphql-servlet.version>9.0.1</graphql-servlet.version>
<graphql-federation-support.version>0.4.0</graphql-federation-support.version>
In user service, you need to return a pojo of the type client, with a getter for a linkeduser (only the extends fields need to be present):
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return new Client((String) id, getSingleUser((String) id));
}
}
Also the resolveTypeEntity needs to resolve to said client
I want to list the student details calling the java api which have below response. I want the values from data which will have multiple values.Below is the method i am calling in component.ts class.
ngOnInit() {
this.loading = true
this.httpService.get('/student').subscribe(
result => {
console.log('received results');
this.loading = false;
this.scouts = result;
},
error => {
console.log('failed');
this.loading = false;
}
``
This is the api response.
``
{
data: [
{
id: 101,
name: "John doe",
status: "enrolled",
}
],
errors: [ ],
warnings: [ ],
meta: { }
}
```
I tried to using this as html code but this won't work and in the component part i have called the httpservice with get request in ngOnInit part.
``
<tr *ngFor="let student of students">
<td>{{student.id}}</td>
<td>{{student.name}}</td>
<td>{{student.status}}</td>
```
Please can you guide me how can i get the student details from data part and list it in front end. Thank you.
From Best practices:
Use url at the service level (since it's a constant used only inside the service)
Define a StudentResponce model so that you can your json response.
Define the Student model that will be a property of StudentReponse
then your component becomes
ngOnInit() {
this.loading = true;
this.httpService.getStudents().subscribe(
result => {
console.log('received results');
this.loading = false;
this.scouts = result.data;
/*or this.scouts = [...result.data] */
//warning = result.warning
//meta = result.meta
//errors = [...result.errors]
},
error => {
console.log('failed');
this.message = error.message;
this.loading = false;
});
}
and the service
const url = "http://replace_with_service_url";
#Injectable()
export class MyHttpService {
constructor(private client: HttpClient) {
}
getStudents(): Observable<StudentResponse>{
return this.client.get<StudentResponse>(url);
}
}
export class StudentResponse{
data: Student[];
warning: any;
errors: any[];
meta: any;
}
export class Student{
id: number;
name= "";
status: string /*or enum "enrolled"*/;
}
you can replace your service url in this code to test
Just change this assignment:
this.scouts = result;
to
this.scouts = result.data;
In my Angular 4 application the is a service as below which maintains booking object list. This booking list is a Subject.
//imports
#Injectable()
export class BookingTreeService {
selBooking: Booking;
bookingList: Array<Booking> = [];
bkgList$: Observable<Array<Booking>>;
private bkgListSubject: Subject<any>;
constructor( private resHttp: ResHttpService) {
this.bkgListSubject = new Subject<Array<Booking>[]>();
this.bkgList$ = this.bkgListSubject.asObservable();
}
loadBooking(bookingId:number): Observable<any> {
let item = this.bookingList.filter(b => b.bookingId === bookingId);
if (item && item.length > 0) {
return this.retrieveBooking(bookingId);
// here, I don't want to call http call. Need to update the booking tree
}
else {
return this.loadBookingAndLock(bookingId);
}
}
loadBookingAndLock(bookingId: any): Observable<any> {
return this.resHttp.loadBookingAndLock(bookingId)
.map(response => {
//handle response
})
.catch(this.handleError);
}
retrieveBooking(bookingId: any): Observable<any> {
return this.resHttp.retrieveBooking(bookingId)
.map(response => {
//handle response
})
.catch(this.handleError);
}
addBooking(booking: Booking) {
this.bookingList.push(booking);
this.updateBookingTree(booking);
}
updateBookingTree(booking: Booking):void {
this.bookingList.map((b:Booking) => {
b.active = b.bookingId === booking.bookingId;
});
this.bkgListSubject.next(this.bookingList);
}
}
In the component I call the loadBooking inside ngOnInit as below.
loadBooking() {
this.paramsSubscription = this.route.params
.switchMap((params: Params) => this.bkgTreeService.loadBooking(+params['id']))
.subscribe(
response => {
},
(error) => {
console.log(error);
}
);
}
If the selected booking is already includes in the booking tree don't want to call http request again, only want to update the booking tree. But inside the switchMap it accepts only Observable. So that how could it be handled?
Any suggestions are appreciated.
Here what i understood is you want to cache the data, so for that you can use Rxjs Caching using shareReplay.
More details at :- https://blog.thoughtram.io/angular/2018/03/05/advanced-caching-with-rxjs.html
You must store the booking list using "tap" when you make the call
loadBookingAndLock(bookingId: any): Observable<any> {
return this.resHttp.loadBookingAndLock(bookingId)
//When the observable finished
.pipe(tap(response=>{
//we concat the response to the bookingList
if (!this.bookingList.find(b=>b.bookingId==bookingId)
this.bookingList.concat(response);
}))
.catch(this.handleError);
}
I am new to angular2, and we have a basic running application and I need to write a new login page for this application.
I looked at this tutorial and pretty much am able to replicate this same as standalone:
http://4dev.tech/2016/03/login-screen-and-authentication-with-angular2/
but when I looked to integrate with existing application, I see already different home page defined in app.component, should I rename this app.component to a new component and redirect to it from login component.
what would be the best way to integrate this with minimal changes..best practices around this
you have to create a loging component and define the the login in route configuration like this
export const routes:RouterConfig = [
{path: 'login', component: LoginComponent},
{path: '', component: LoginComponent}] //default to login page
in login component. i code like
export class LoginComponent implements OnInit {
private jwtHelper:JwtHelper = new JwtHelper();
messages:String[] = [];
localUser = {
username: '',
password: ''
}
constructor(private _service:LoginService, private _router:Router) {
}
login() {
this._service.login(this.localUser).then((data) => {
if (data) {
this._router.navigate(['/companies']);
}
},
(error) => {
this.messages = error;
});
}
clearfields() {
this.localUser.username = '';
this.localUser.password = '';
this.messages = [];
}
ngOnInit():any {
if (window.localStorage.getItem('auth_key') === undefined) {
console.log("window.localStorage.getItem('auth_key'): " + window.localStorage.getItem('auth_key'));
}
else if (window.localStorage.getItem('auth_key') != null && !this.jwtHelper.isTokenExpired(window.localStorage.getItem('auth_key'))) {
this._router.navigate(['/companies']);
}
}
after login you can navigate to any page abased on your need