Check Id_token or access_token on identityserver4 - identityserver4

I am using Identityserver4 with my Angular application implicit flow. currently, im facing some Authentication issues and for that i need to verify the token Generated by the IDS and the token i have in my angular client after authentication.
I am not How to get the Token in IdentityServer while authenticating a client ?
here is my oidc related code in angular:
import { Injectable } from '#angular/core';
import { UserManager, UserManagerSettings, User, WebStorageStateStore } from 'oidc-client';
import { AppConfigService } from '../../app-config.service';
// import { Config } from '../../../app-config';
#Injectable()
export class AuthService {
private manager;
private user: User = null;
constructor(private appConfig: AppConfigService) {
this.manager = new UserManager(getClientSettings(appConfig));
this.manager.getUser().then(user => {
this.user = user;
});
this.manager.events.addUserLoaded( user => {
this.user = user;
});
}
public get UserId() {
if (this.user && this.user != null && this.user.profile && this.user.profile.sub) {
return this.user.profile.sub;
}
return null;
}
public get User() {
if (this.user && this.user != null && this.user.profile && this.user.profile != null) {
return this.user.profile;
}
return null;
}
public isLoggedIn(): boolean {
return this.user != null && !this.user.expired;
}
public getClaims(): any {
return this.user.profile;
}
public getAuthorizationHeaderValue(): string {
//
return `${this.user.token_type} ${this.user.access_token}`;
}
public startAuthentication(): Promise<void> {
return this.manager.signinRedirect();
}
public signout(): Promise<void> {
localStorage.removeItem('user');
return this.manager.signoutRedirect();
}
public signoutRedirectCallback(): Promise<any> {
// localStorage.removeItem('user');
return this.manager.signoutRedirectCallback();
}
public completeAuthentication(): Promise<void> {
return this.manager.signinRedirectCallback().then(user => {
this.user = user;
});
}
public silentRefresh(): Promise<void> {
console.log('before');
return this.manager.signinSilentCallback(); // .then(t => console.log(t))
// .catch((err) => {
// console.log(err);
// });
}
public signinSilent(): Promise<User> {
return this.manager.signinSilent();
}
}
export function getClientSettings(appConfig: AppConfigService):
UserManagerSettings {
console.log('in auth service', appConfig.getConfig('IDS_ENDPOINT'));
return {
authority: appConfig.getConfig('IDS_ENDPOINT'), // 'https://localhost:44360/',
client_id: appConfig.getConfig('IDS_CLIENT'),
redirect_uri: appConfig.getConfig('IDS_redirectUrl'), // 'http://localhost:4200/auth-callback',
post_logout_redirect_uri: appConfig.getConfig('IDS_LogoutUrl'), // 'http://localhost:4200/',
response_type: 'id_token token',
scope: 'openid profile roles smsapi country subscriptionlevel permissions',
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
accessTokenExpiringNotificationTime: 60,
silent_redirect_uri: appConfig.getConfig('IDS_refreshUrl')
// userStore: new WebStorageStateStore({ store: window.localStorage })
};
}
Any Help?

Related

How a SPA with oidc-client could send token to external web api project

I am developing a SPA entirely in node.js/react that talks to an external identity server instance (we don't manage it) using the oidc-client module.
As for the client SPA itself, I was able to manage the login and logout phases.
This SPA, however, calls an external web api project (managed by us) developed in .net framework 4.8 to get the data
The people I am talking to wanted to register my SPA as a client in their identityserver and not the web api project.
I wanted to know if once I get the token on the client, I can manage a flow of permissions based on that token on my web api.
This is my authService and its configuration:
// configuration:
public static configDev: UserManagerSettings = {
authority: 'https://idservices.servicehosting.it/,
client_id: '<the client id of my spa>',
client_secret: '<the client secret of my spa>',
redirect_uri: 'https://localhost:9099/signin-callback',
silent_redirect_uri: 'https://localhost:9099/silent-renew',
response_type: 'code',
scope: 'openid profile email roles',
post_logout_redirect_uri: 'https://localhost:9099/signout-oidc',
loadUserInfo: true,
revokeAccessTokenOnSignout: true,
};
// service
import { UserManager, Log, User } from 'oidc-client';
import Constants from '../helpers/const/Constants';
export const AuthenticationResultStatus = {
Redirect: 'redirect',
Success: 'success',
Fail: 'fail',
};
export type UserState = {
redirectPath?: string;
};
export type AuthResponse = {
status: string;
message?: string;
userState?: UserState;
};
function buildError(message: string): AuthResponse {
return { status: AuthenticationResultStatus.Fail, message };
}
function buildSuccess(userState: UserState): AuthResponse {
return { status: AuthenticationResultStatus.Success, userState };
}
function buildRedirect(): AuthResponse {
return { status: AuthenticationResultStatus.Redirect };
}
type CallbackSub = {
callback: () => void;
subscription: number;
};
export class AuthService {
private userManager: UserManager;
private callbacks: CallbackSub[] = [];
private nextSubscriptionId = 0;
private currentUser: User = null;
public constructor() {
this.userManager = new UserManager(Constants.globalOidcConfig);
// Logger
Log.logger = console;
Log.level = Log.DEBUG;
this.userManager.events.addAccessTokenExpiring(() => {
console.log('token expiring');
void this.trySilentSignIn();
});
this.userManager.events.addAccessTokenExpired(() => {
console.log('token expired');
void this.userManager.removeUser().then(() => {
this.updateState(null);
});
});
this.userManager.events.addSilentRenewError((e) => {
console.log('silent renew error', e.message);
});
this.userManager.events.addUserLoaded((user) => {
console.log('user loaded', user);
});
this.userManager.events.addUserUnloaded(() => {
console.log('user unloaded');
});
this.userManager.events.addUserSignedIn(() => {
console.log('user logged in to the token server');
});
this.userManager.events.addUserSignedOut(() => {
console.log('user logged out of the token server');
});
}
updateState(user: User): void {
this.currentUser = user;
this.notifySubscribers();
}
subscribe(callback: () => Promise<void>): number {
this.callbacks.push({
// eslint-disable-next-line #typescript-eslint/no-misused-promises
callback,
subscription: this.nextSubscriptionId,
});
this.nextSubscriptionId += 1;
return this.nextSubscriptionId - 1;
}
unsubscribe(subscriptionId: number): void {
const subscriptionIndex = this.callbacks
.map((element, index) => (element.subscription === subscriptionId ? { found: true, index } : { found: false }))
.filter((element) => element.found === true);
if (subscriptionIndex.length !== 1) {
throw new Error(`Found an invalid number of subscriptions ${subscriptionIndex.length}`);
}
this.callbacks.splice(subscriptionIndex[0].index, 1);
}
notifySubscribers(): void {
for (let i = 0; i < this.callbacks.length; i++) {
const { callback } = this.callbacks[i];
callback();
}
}
async getUser(): Promise<User> {
if (this.currentUser?.profile == null) {
this.currentUser = await this.userManager.getUser();
}
return this.currentUser;
}
async getAccessToken(): Promise<string> {
const user = await this.userManager.getUser();
return user && user.access_token;
}
async trySilentSignIn(): Promise<User> {
await this.userManager
.signinSilent()
.then((user: User) => {
this.updateState(user);
return user;
})
.catch((error: Error) => {
void this.userManager.getUser().then((user: User) => {
console.log('silent renew error', error.message);
this.updateState(user);
return undefined;
});
});
return undefined;
}
// We try to authenticate the user in two different ways:
// 1) We try to see if we can authenticate the user silently. This happens
// when the user is already logged in on the IdP and is done using a hidden iframe
// on the client.
// 3) If the method above fails, we redirect the browser to the IdP to perform a traditional
// redirect flow.
async signin(userState: UserState): Promise<AuthResponse> {
try {
const silentUser = await this.userManager.signinSilent({ useReplaceToNavigate: true, state: userState });
this.updateState(silentUser);
return buildSuccess(silentUser.state as UserState);
} catch (silentError) {
// User might not be authenticated, fallback to redirect
console.log('Silent authentication error: ', silentError);
try {
await this.userManager.signinRedirect({ useReplaceToNavigate: true, state: userState });
return buildRedirect();
} catch (redirectError) {
console.log('Redirect authentication error: ', redirectError);
return buildError(redirectError as string);
}
}
}
async completeSignin(url?: string): Promise<AuthResponse> {
try {
const user = (await this.getUser()) || (await this.userManager.signinCallback(url));
const userState = user.state as UserState;
this.updateState(user);
return buildSuccess(userState);
} catch (error) {
console.log('There was an error signing in: ', error);
return buildError('There was an error signing in.');
}
}
// Redirect the browser to the IdP to perform a traditional
// post logout redirect flow.
async signout(): Promise<AuthResponse> {
try {
await this.userManager.signoutRedirect();
return buildRedirect();
} catch (redirectSignOutError) {
console.log('Redirect signout error: ', redirectSignOutError);
return buildError(redirectSignOutError as string);
}
}
async completeSignout(url?: string): Promise<AuthResponse> {
try {
await this.userManager.signoutCallback(url);
this.updateState(null);
return buildSuccess(null);
} catch (error) {
console.log('There was an error trying to log out ', error);
return buildError(error as string);
}
}
}
const authService = new AuthService();
export default authService;
As for the web api, for now I have only added this in my OWIN startup class:
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new List<string> { "openid", "profile", "email", "roles" },
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateIssuerSigningKey = false,
ValidIssuer = "https://localhost:9099"
ValidAudience = "https://localhost:9099", // <- I think this is wrong
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ConfigurationManager.AppSettings["JwtKey"]))
}
});
and this in WebApiConfig to add the bearer token authentication filter instead of its default one:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
how could i set my webapi to correctly receive the token from my client (the spa)? thank you!
`

Why doesn't this simple API call to ASP.net work within ReactJS?

UsersController.cs
namespace testlol
{
[ApiController]
public class UsersController : ControllerBase
{
private readonly ILogger<UsersController> _logger;
static readonly Models.IUserRepository repository = new Models.UserRepository();
public UsersController(ILogger<UsersController> logger)
{
_logger = logger; //what is this ?
}
[HttpGet]
[Route("api/users")]
public IEnumerable<Models.UserModel> GetAllUsers()
{
return repository.GetAll();
}
[HttpPost]
[Route("api/user")]
[Consumes("application/json")]
public Models.UserModel PostUser(Models.UserModel item)
{
return repository.Add(item);
}
}
}
Where I then use the service in react:
userService.js
export async function getAllUsers() {
const response = await fetch('/api/users');
return await response.json();
}
Then, I try to call it in a component:
import { getAllUsers } from "../services/userService.js"
...
useEffect(() => {
const getUserList = () => {
console.log("Getting user list ..");
getAllUsers()
.then(userList => {
console.log("User list: ")
console.log(userList)
setUsers(userList)
});
}
getUserList()
}, [])
This always yields the error:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
I'm not sure how the program isn't working. I saw that it could be a blank string, but I'm also currently storing some hardcoded values in another file:
public UserRepository()
{
Add(new UserModel { email = "test#mail.com", password ="password" });
Add(new UserModel { email = "admin#mail.com", password ="admin" });
}

Failed assertion : 'email != null'?

does anyone know why my result is null? When I want to SignUp the debug console display : Failed assertion: line 183 pos 12: 'email != null': is not true. And it displays null. This is my SignUp code when I press SignUp Button and the other code ist my auth class. If anyone knows please comment.
Future <void> signUp() async {
if(_formKey.currentState.validate()){
setState(() {
isLoading = true;
});
await authService.signUpWithEmailAndPassword(_email,
_password).then((result) {
if(result != null){
Map<String,String> userDataMap = {
"Username" : _Username,
"Email" : _email,
"Nation" : _Nation,
};
databaseService.addUserInfo(userDataMap);
HelperFunction.saveUserLoggedInSharedPreference(true);
HelperFunction.saveUserNameSharedPreference(_Username);
HelperFunction.saveUserEmailSharedPreference(_email);
HelperFunction.saveUserNationSharedPreference(_Nation);
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => LoadingBarForUserCreation()
));
}else{
print(result);
}
}).catchError((e){
_showSettingPanelForEmail();
});
}
AuthService
class AuthService{
final FirebaseAuth _auth = FirebaseAuth.instance;
Userf _userFromFirebaseUser(User user){
return user != null ? Userf(uid: user.uid) : null;
}
Stream<Userf> get user {
return _auth.onAuthStateChanged
.map(_userFromFirebaseUser);
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
User user = (await _auth.signInWithEmailAndPassword(
email: email, password: password)).user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
Future signUpWithEmailAndPassword(String email, String password) async {
try {
User user = (await _auth.createUserWithEmailAndPassword(
email: email, password: password)).user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
}

404 on webapi custom route

I am really struggling with this
I have an action controller like this:
[HttpPost]
[Route("api/SiteCollections/SetSiteCollectionActive")]
public async Task<IHttpActionResult> SetSiteCollectionActive(string siteCollectionUrl)
{
var siteCollectionsStore = CosmosStoreFactory.CreateForEntity<TenantManagementWebApi.Entities.SiteCollection>();
var allSiteCollections = await siteCollectionsStore.Query().Where(x => x.Title != null).ToListAsync();
foreach (TenantManagementWebApi.Entities.SiteCollection sc in allSiteCollections)
{
sc.Active = false;
await siteCollectionsStore.UpdateAsync(sc);
}
var siteCollection = await siteCollectionsStore.Query().FirstOrDefaultAsync(x => x.Id == siteCollectionUrl);
if (siteCollection == null)
{
return NotFound();
}
siteCollection.Active = true;
var result = await siteCollectionsStore.UpdateAsync(siteCollection);
return Ok(result);
}
and from a reactjs application, I am trying to do the following:
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
if(selectedRows[0].Url != undefined){
console.log(selectedRows[0].Url);
const options = {
method: 'post'
};
adalApiFetch(fetch, "/SiteCollections/SetSiteCollectionActive?siteCollectionUrl="+selectedRows[0].Url.toString(), options)
.then(response =>{
if(response.status === 200){
Notification(
'success',
'Site Collection set to active',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Site Collection not activated',
error
);
console.error(error);
});
}
},
getCheckboxProps: record => ({
type: Radio
}),
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
);
However, I always get this error:
everything seems correct
Route Config
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

TypeScript - I can't call my json with http get using Angular 2

I want to develop a file configuration of json and it is called with http get the constructor and return the value I want the config file to another component. But when return gives me value undefined.
My Config.json
[ {"urlServer": "http://localhost:56877"}]
My Config.Service
export class configService
{
url: string;
constructor(public _http: Http)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try {
return this._http.get('/app/config.json',
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <configModel>res.json();
this.url = data.urlServer;
JSON.stringify(this.url);
});
}
catch (ex) {
logger.registarErros('configService', ex);
}
}
returnConfig()
{
return this.url;
}
Now my other Component
constructor(public _http: Http, public config: configService)
{
this.token = sessionStorage.getItem('token');
this.username = sessionStorage.getItem('username');
}
login(username: String, password: String)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try
{
alert(this.config.url);
return this._http.post('http://localhost:56877/api/Login/EfectuaLogin', JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return Observable.of('authObservable');
});
}
catch (ex) {
logger.registarErros('authentication', ex);
}
}
I no longer know how to solve the problem, I need your help, I'm not very experienced with Angular 2.
Thanks very much.
The problem here is that the config is load asynchronously. You could use something like that leveraging the flatMap operator:
#Injectable()
export class ConfigService {
urlServer:string;
constructor(public _http: Http) {
}
getConfig() {
if (this.urlServer) {
return Observable.of(this.urlServer);
}
return this._http.get('/app/config.json', {
headers: contentHeaders
})
.map((res: any) => {
let data = <configModel>res.json();
return data.urlServer;
}).do(urlServer => {
this.urlServer = urlServer;
});
}
}
and in your component:
login(username: String, password: String) {
return this.configService.getConfig().flatMap(urlServer => {
this._http.post('http://localhost:56877/api/Login/EfectuaLogin',
JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return data; // or something else
});
}
});
}
Another approach would be boostrap asynchronously after having loaded the configuration:
var app = platform(BROWSER_PROVIDERS)
.application([BROWSER_APP_PROVIDERS, appProviders]);
service.getConfig().flatMap((url) => {
var configProvider = new Provider('urlServer', { useValue: urlServer});
return app.bootstrap(appComponentType, [ configProvider ]);
}).toPromise();
See this question for the second approach:
angular2 bootstrap with data from ajax call(s)
You can go further by mixing the last approach with a CustomRequestOptions:
import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from 'angular2/http';
export class CustomRequestOptions extends BaseRequestOptions {
merge(options?:RequestOptionsArgs):RequestOptions {
options.url = 'http://10.7.18.21:8080/api' + options.url;
return super.merge(options);
}
}
See this question:
Angular 2 - global variable for all components

Resources