I am following this auth0 tutorial for a react/express + jwt webpage.
Everything seems ok. Login/logout, accessing secure page all good.
Except I can bypass the login with a fake token. If I just go to jwt.io and generate a token that hasn't expired yet, I can access the secure page without actually logging in. What am I missing here?
This is the part of the code that handles authentication:
./utils/AuthService.js
const ID_TOKEN_KEY = 'id_token';
const ACCESS_TOKEN_KEY = 'access_token';
const CLIENT_ID = 'auht0 client id';
const CLIENT_DOMAIN = 'foo.eu.auth0.com';
const REDIRECT = 'http://localhost:3000/callback';
const SCOPE = 'openid profile ';
const AUDIENCE = 'https://foo.eu.auth0.com/api/v2/';
var auth = new auth0.WebAuth({
clientID: CLIENT_ID,
domain: CLIENT_DOMAIN
});
export function login() {
auth.authorize({
responseType: 'token id_token',
redirectUri: REDIRECT,
audience: AUDIENCE,
scope: SCOPE
});
}
export function logout() {
clearIdToken();
clearAccessToken();
browserHistory.push('/');
}
export function requireAuth(nextState, replace) {
if (!isLoggedIn()) {
replace({pathname: '/'});
}
}
export function getIdToken() {
return localStorage.getItem(ID_TOKEN_KEY);
}
export function getAccessToken() {
return localStorage.getItem(ACCESS_TOKEN_KEY);
}
function clearIdToken() {
localStorage.removeItem(ID_TOKEN_KEY);
}
function clearAccessToken() {
localStorage.removeItem(ACCESS_TOKEN_KEY);
}
// Helper function that will allow us to extract the access_token and id_token
function getParameterByName(name) {
let match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
// Get and store access_token in local storage
export function setAccessToken() {
let accessToken = getParameterByName('access_token');
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);
}
// Get and store id_token in local storage
export function setIdToken() {
let idToken = getParameterByName('id_token');
localStorage.setItem(ID_TOKEN_KEY, idToken);
}
export function isLoggedIn() {
const idToken = getIdToken();
return !!idToken && !isTokenExpired(idToken);
}
function getTokenExpirationDate(encodedToken) {
const token = decode(encodedToken);
if (!token.exp) { return null; }
const date = new Date(0);
date.setUTCSeconds(token.exp);
return date;
}
function isTokenExpired(token) {
const expirationDate = getTokenExpirationDate(token);
return expirationDate < new Date();
}
The part you've posted just stores / retrieves the token from the local storage.
The authentication itself is handled on the server side
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
// YOUR-AUTH0-DOMAIN name e.g prosper.auth0.com
jwksUri: "https://{YOUR-AUTH0-DOMAIN}/.well-known/jwks.json"
}),
// This is the identifier we set when we created the API
audience: '{YOUR-API-AUDIENCE-ATTRIBUTE}',
issuer: '{YOUR-AUTH0-DOMAIN}',
algorithms: ['RS256']
});
The sever-side funtionality MUST check the JWT token signature. Without the private key you won't be able to generate a valid signature of the JWT token.
If you really can bypass the authentication, it means the server has serious security issue (the signature is not properly validated). Maybe for test/demo services it is not implemented.
Related
I am trying to implement the OAuth authentication for HERE REST API following this documentation: https://developer.here.com/documentation/identity-access-management/dev_guide/topics/sdk.html
My problem is that when I send the POST request with the OAuth Header and Body, the response I receive from the server is: Signature mismatch. Authorization signature or client credential is wrong.
I probed my credentials using the OAuth1.0 authorization in Postman and the HERE API responds with a valid access token. Hence my credentials values are correct.
In addition to the official documentation, I followed recommandations published in various Stack Overflow answers, especially the encoding of all parameters (encodeURI).
I suspect the CryptoJS npm package to be the source of my problem. The result of the HmacSHA256 cryptographic function is very different from various online HmaSHA256 that I tried with my baseString as parameter. However, the crypto-js library (+ #type/crypto-js for Typescript) seems to be the reference for React Typescript crypto functions. I couldn't figure out where I got it wrong :/
Following is my implementation of the OAuth method to get my access token.
I am using :
"#types/crypto-js": "^4.1.1",
"crypto-js": "^4.1.1",
<node.version>v14.14.0</node.version>
<npm.version>v6.14.8</npm.version>
HERE_ACCESS_KEY = <here.access.key>;
HERE_ACCESS_SECRET = <here.access.key.secret>;
HERE_AUTH_URL = "https://account.api.here.com/oauth2/token";
My frontend code:
import CryptoJS from "crypto-js";
private buildOAuthSignatureBaseString(accessKey: string, nonce: string, hashMethod: string, timestamp: string) {
const baseStringParameters = `grant_type=client_credentials&oauth_consumer_key=${accessKey}&oauth_nonce=${nonce}&oauth_signature_method=${hashMethod}&oauth_timestamp=${timestamp}&oauth_version=1.0`;
return `POST&${encodeURIComponent(this.HERE_AUTH_URL)}&${encodeURIComponent(baseStringParameters)}`;
}
private buildHereOauthSignature(accessKey: string, nonce: string, hashMethod: string, timestamp: string) {
const baseString = this.buildOAuthSignatureBaseString(accessKey, nonce, hashMethod, timestamp);
const signingKey = `${encodeURIComponent(this.HERE_ACCESS_SECRET)}&`;
const hashedBaseString = CryptoJS.HmacSHA256(baseString, signingKey);
const base64hash = CryptoJS.enc.Base64.stringify(hashedBaseString);
return encodeURIComponent(base64hash);
}
public getHereMapsApiAccessToken() {
const hashMethod = encodeURIComponent("HMAC-SHA256");
const accessKey = encodeURIComponent(this.HERE_ACCESS_KEY);
const nonce = encodeURIComponent(new Buffer(random(8)).toString("base64"));
const timestamp = Math.floor(new Date().getTime() / 1000).toString(10);
const signature = this.buildHereOauthSignature(accessKey, nonce, hashMethod, timestamp);
const headers = new Headers({
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": `OAuth oauth_consumer_key="${accessKey}", oauth_nonce="${nonce}", oauth_signature_method="${hashMethod}", oauth_timestamp="${timestamp}", oauth_version="1.0", oauth_signature="${signature}"`
});
const method = HTTP_METHODS.POST;
const options = {headers, method, body: JSON.stringify({grant_type: "client_credentials"})};
return fetch("https://account.api.here.com/oauth2/token", options)
.then((response) => {
console.log(response);
return true;
}).catch((error) => {
console.log(error);
return false;
});
}
Thanks in advance !
You can try it from this example https://github.com/alexisad/reactjs_oauth2
Main code is in src/App.jsx:
/**
* Gets an access token for a given access key and secret.
* #param {*} access_key
* #param {*} access_secret
*/
export const getToken = (access_key, access_secret) => {
let url = "https://account.api.here.com/oauth2/token";
let key = encodeURI(access_key);
let secret = encodeURI(access_secret);
let nonce = btoa(Math.random().toString(36)).substring(2, 13);
let timestamp = Math.floor(Date.now()/1000);
let normalizedUrl = encodeURIComponent(url);
let signing_method = encodeURI("HMAC-SHA256");
let sig_string = "oauth_consumer_key="
.concat(key)
.concat("&oauth_nonce=")
.concat(nonce)
.concat("&oauth_signature_method=")
.concat(signing_method)
.concat("&oauth_timestamp=")
.concat(timestamp)
.concat("&").concat("oauth_version=1.0");
let normalised_string = "POST&".concat(normalizedUrl).concat("&").concat(encodeURIComponent(sig_string));
let signingKey = secret.concat("&");
let digest = CryptoJS.HmacSHA256(normalised_string, signingKey);
let signature = CryptoJS.enc.Base64.stringify(digest);
let auth = 'OAuth oauth_consumer_key="'
.concat(key)
.concat('",oauth_signature_method="')
.concat(signing_method)
.concat('",oauth_signature="')
.concat(encodeURIComponent(signature))
.concat('",oauth_timestamp="')
.concat(timestamp)
.concat('",oauth_nonce="')
.concat(nonce)
.concat('",oauth_version="1.0"')
return axios({
method: 'post',
url: url,
data: JSON.stringify({grantType: "client_credentials"}),
headers: {
'Content-Type': "application/json",
'Authorization': auth
}
});
}
/**
* Send request for a given token and url.
* #param {*} token
* #param {*} url
*/
export const sendReqOauth2 = (token, url) => {
return axios({
method: 'get',
url: url,
headers: {
'Authorization': 'Bearer ' + token
}
});
};
We have users complaining because they are redirected to the login page of the Identity Server while in the middle of their work (and thus losing their current work). We have endeavoured to configure a sliding expiration, so I'm not sure why this is happening.
I realise there is quite a bit of code in this post. But there are a lot of moving parts and I want to give as much information as possible.
This behavour is arratic and it is hard to report an exact reproducable event. In my testing, I've been ejected at random times and it is hard to understand whether it has any relationship to any of the cofigurations which I have set. In my mind, I should not be ejected at all, as a silent sign-in is always sent during the addAccessTokenExpiring event.
The setup that we have is:
an Idp (using IdentityServer 4)
A client app, implemented using Vue.js (using Typescript)
An API, written in ASP.NET Core 5
The config and auth service which we have written are:
auth.config.ts
import { Log, UserManagerSettings, WebStorageStateStore } from "oidc-client";
import AppConfig from "./invariable/app.config";
/* eslint-disable */
class AuthConfig {
public settings: UserManagerSettings;
private baseUrl: string;
constructor() {
this.baseUrl = AppConfig.RunTimeConfig.VUE_APP_APPURL || process.env.VUE_APP_APPURL;
this.settings = {
userStore: new WebStorageStateStore({ store: window.localStorage }),
authority: AppConfig.RunTimeConfig.VUE_APP_IDPURL || process.env.VUE_APP_IDPURL,
client_id: AppConfig.RunTimeConfig.VUE_APP_CLIENTID || process.env.VUE_APP_CLIENTID,
client_secret: AppConfig.RunTimeConfig.VUE_APP_CLIENTSECRET || process.env.VUE_APP_CLIENTSECRET,
redirect_uri: this.baseUrl + process.env.VUE_APP_AUTHCALLBACK,
automaticSilentRenew: false,
silent_redirect_uri: this.baseUrl + process.env.VUE_APP_SILENTREFRESH,
response_type: "code",
response_mode: "query",
scope: "our_scopes",
post_logout_redirect_uri: this.baseUrl + process.env.VUE_APP_SIGNOUT_CALLBACK,
filterProtocolClaims: true,
loadUserInfo: true,
revokeAccessTokenOnSignout: true,
staleStateAge: 300, // should match access_token lifetime.
};
}
}
/* eslint-enable */
const authConfig = new AuthConfig();
export default authConfig;
auth.service.ts
import { UserManagerSettings, User, UserManager } from "oidc-client";
import authConfig from "#/config/auth.config";
import axios, { AxiosResponse } from "axios";
import { Ajax } from "#/config/invariable/ajax";
import AccessClaim from "#/domain/general/accessclaim";
import _ from "lodash";
import store from "#/store";
import StoreNamespaces from "#/config/invariable/store.namespaces";
import Token from "#/store/token/token";
export class AuthService {
private userManager: UserManager;
private tokenStore: string;
constructor(private settings: UserManagerSettings) {
this.settings = settings;
this.userManager = new UserManager(this.settings);
this.tokenStore = StoreNamespaces.tokenModule;
}
public addEvents(): void {
this.userManager.events.addUserSignedOut(() => {
this.signInAgain();
});
this.userManager.events.addAccessTokenExpired(() => {
console.log("Token expired");
this.clearLocalState();
console.log("Stale state cleaned up");
});
this.userManager.events.addAccessTokenExpiring(() => {
console.log("Access token about to expire.");
this.signInAgain();
});
this.userManager.events.addSilentRenewError(() => {
// custom logic here
console.log("An error happened whilst silently renewing the token.");
});
}
public clearLocalState(): Promise<void> {
return this.userManager.clearStaleState();
}
public getUserOnLoad(): Promise<User> {
return this.userManager.getUser().then((user) => {
if (!_.isNil(user) && !user.expired) {
console.log("first load sign-in");
const decodedIdToken = user.profile;
if (!_.isNil(decodedIdToken.store) && !_.isArray(decodedIdToken.store)) decodedIdToken.store = [decodedIdToken.store];
if (!_.isNil(decodedIdToken.classification) && !_.isArray(decodedIdToken.classification)) decodedIdToken.classification = [decodedIdToken.classification];
if (!_.isNil(decodedIdToken.location) && !_.isArray(decodedIdToken.location)) decodedIdToken.location = [decodedIdToken.location];
if (!_.isArray(decodedIdToken.app)) decodedIdToken.app = [decodedIdToken.app];
const token = new Token();
token.accessToken = user.access_token;
token.idToken = user.id_token;
token.storeClaims = decodedIdToken.store || [];
token.userType = decodedIdToken.usertype;
token.isLoggedIn = user && !user.expired;
token.app = decodedIdToken.app;
token.userName = decodedIdToken.name ?? "Unknown User";
store.dispatch(`${this.tokenStore}/setToken`, token);
return user;
} else {
return this.signInAgain();
}
});
}
public async getUserIfLoggedIn(): Promise<User | null> {
const currentUser: User | null = await this.userManager.getUser();
const loggedIn = currentUser !== null && !currentUser.expired;
return loggedIn ? currentUser : null;
}
public async isLoggedIn(): Promise<boolean> {
const currentUser: User | null = await this.userManager.getUser();
return currentUser !== null && !currentUser.expired;
}
public login(): Promise<void> {
return this.userManager.signinRedirect();
}
public logout(): Promise<void> {
return this.userManager.signoutRedirect();
}
public getAccessToken(): Promise<string> {
return this.userManager.getUser().then((data: any) => {
return data.access_token;
});
}
public signInAgain(): Promise<User> {
return this.userManager
.signinSilent()
.then((user) => {
console.log("silent sign-in");
const decodedIdToken = user.profile;
if (!_.isNil(decodedIdToken.store) && !_.isArray(decodedIdToken.store)) decodedIdToken.store = [decodedIdToken.store];
if (!_.isNil(decodedIdToken.classification) && !_.isArray(decodedIdToken.classification)) decodedIdToken.classification = [decodedIdToken.classification];
if (!_.isNil(decodedIdToken.location) && !_.isArray(decodedIdToken.location)) decodedIdToken.location = [decodedIdToken.location];
if (!_.isArray(decodedIdToken.app)) decodedIdToken.app = [decodedIdToken.app];
const token = new Token();
token.accessToken = user.access_token;
token.idToken = user.id_token;
token.storeClaims = decodedIdToken.store || [];
token.userType = decodedIdToken.usertype;
token.isLoggedIn = user && !user.expired;
token.app = decodedIdToken.app;
token.userName = decodedIdToken.name ?? "Unknown User";
store.dispatch(`${this.tokenStore}/setToken`, token);
return user;
})
.catch((err) => {
console.log("silent error");
console.log(err);
this.login();
return err;
});
}
public getAccessClaims(userDetails: any): Promise<AxiosResponse<any>> {
return axios.post(`${Ajax.appApiBase}/PermittedUse/GetAccessesForUser`, userDetails).then((resp: AxiosResponse<any>) => {
return resp.data;
});
}
public getPermissions(userDetails: any, siteId: number | null): Promise<AxiosResponse<any>> {
return axios.get(`${Ajax.appApiBase}/PermittedUse/GetPermissions/${siteId ?? 0}`).then((resp: AxiosResponse<any>) => {
return resp.data;
});
}
public constructAccess(userType: string, claims: Array<AccessClaim>): Array<AccessClaim> {
switch (userType) {
case "storeadmin":
case "storeuser":
return _.filter(claims, (claim) => {
return claim.claim === "store";
});
case "warduser":
return _.filter(claims, (claim) => {
return claim.claim === "classification";
});
}
return Array<AccessClaim>();
}
public getBookableLocations(userType: string, claims: Array<AccessClaim>): Array<AccessClaim> {
switch (userType) {
case "storeadmin":
case "storeuser":
return _.filter(claims, (claim) => {
return claim.claim === "store";
});
case "warduser":
return _.filter(claims, (claim) => {
return claim.claim === "location";
});
}
return Array<AccessClaim>();
}
}
export const authService = new AuthService(authConfig.settings);
On the Idp, our client configuration is:
ClientName = IcClients.Names.ConsumablesApp,
ClientId = IcClients.ConsumablesApp,
RequireConsent = false,
AccessTokenLifetime = TokenConfig.AccessTokenLifetime, // 300 for test purposes
IdentityTokenLifetime = TokenConfig.IdentityTokenLifetime, // 300
AllowOfflineAccess = true,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding,
UpdateAccessTokenClaimsOnRefresh = true,
RequireClientSecret = true,
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
AllowAccessTokensViaBrowser = true,
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = new List<string>
{
"https://localhost:44336/authcallback.html",
"https://localhost:8090/authcallback.html",
"https://localhost:44336/silent-refresh.html",
"https://localhost:8090/silent-refresh.html"
},
PostLogoutRedirectUris = new List<string>
{
"https://localhost:44336/signout-callback-oidc.html",
"https://localhost:8090/signout-callback-oidc.html"
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.LocalApi.ScopeName,
IcAccessScopes.IcAccessClaimsScope,
IdentityResources.UserDetails,
IcAccessScopes.ConsumablesScope
},
ClientSecrets = { new Secret("oursecret".Sha256())}
At the Idp, we are using ASP.NET Core Identity:
services.AddIdentity<IdpUser, IdentityRole<int>>()
.AddEntityFrameworkStores<IdpDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = cookieDuration; // set to 1 hour
options.SlidingExpiration = true;
});
My expectation is that the sliding window should be extended every 5 minutes, as the user should be signed in again silently before the token expires.
When monitoring the IDP in my dev environment, one thing which I did note is that the checksession call is only being made once, when the user logs in. The wiki says that the checksession call should happen every 2s (by default). I have not changed this default (not knowingly). I even expressly set the checkSessionInterval property to 2000 to ensure that it was set to 2s.
The other thing I want to set out is the silent refresh html file, as I realise the CSP stuff can play into this:
<head>
<title></title>
<meta http-equiv="Content-Security-Policy" content="frame-src 'self' <%= VUE_APP_IDPURL %>; script-src 'self' 'unsafe-inline' 'unsafe-eval';" />
</head>
<body>
<script src="./oidc-client.min.js"></script>
<script>
(function refresh() {
window.location.hash = decodeURIComponent(window.location.hash);
new Oidc.UserManager({
// eslint-disable-next-line #typescript-eslint/camelcase
response_mode: "query",
userStore: new Oidc.WebStorageStateStore({
store: window.localStorage,
}),
})
.signinSilentCallback()
.then(function() {
console.log("****************************************signinSilentCallback****************************************");
})
.catch(function(err) {
debug;
console.log(err);
});
})();
</script>
</body>
If anyone can shed any light on this, it would be much appreciated.
Some further information. As a test, I set the refrsh time for the token and the cookie lifetime for the identity cookie to both be 10 hours (36,000s).
I am still getting reports of Users being kicked out after 45 minutes.
There's 2 conclusions I came to in resolving this and I would hesitate to say that it is a proper resolution to our problem.
There was a mistake in my Login code. I was using the IdentityServer4 extensions HttpContext.SignInAsync(user, authProperties) to create the session cookie. This is not the way to do things, if using ASP.NET Identity. For 1 reason, it does not include the SecurityStamp claim in the cookie. In their own quickstart, they use the SignInManager to sign in and issue the cookie _signInManager.PasswordSignInAsync(idpUser, model.Password, model.RememberLogin, true).
I needed to turn off the SecurityStamp validation feature in ASP.NET Identity. We had adjudged that we can live without it. You would think there would be a config setting for this, but I was not able to find it. So, at this stage, I have subclassed the UserManager and overridden the SupportsUserSecurityStamp property as follows: public override bool SupportsUserSecurityStamp => false;. Theoretically, this feature will now be turned off. Happy to be corrected if that is not the case or if there is a better way of doing it. (Would love to hear from a member of the ASP.NET team on this).
That's it.
I'm having trouble figuring out why can't I see a prompt to enter API key. I can connect directly without any authentication. Why is the API key ignored?
auth.js file:
function getAuthType() {
return {
type: 'KEY'
};
}
function validateKey(key) {
var url = 'http://myapi.com/endpoint?api_key=' + key;
var options = {
"method": "post",
"contentType":"application/json"
};
var response = JSON.parse(
UrlFetchApp.fetch(url, options)
);
Logger.log(response.data.length > 0)
return response.data.length > 0;
}
function isAuthValid() {
var userProperties = PropertiesService.getUserProperties();
var key = userProperties.getProperty('dscc.key');
return validateKey(key);
}
function resetAuth() {
var userProperties = PropertiesService.getUserProperties();
userProperties.deleteProperty('dscc.key');
}
function setCredentials(request) {
var key = request.key;
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('dscc.key', key);
return {
errorCode: 'NONE'
};
}
function isAdminUser() {
return false;
}
The Logger.log output:
I was using a http url. We've moved our API to https and the problem is solved. Data Studio doesn't show any error messages and skips the auth step. This is very strange.
Edit: A month later, while reviewing the document I noticed that Data Studio is already asking for us an https url.
Each prefix must use https://, not http://. source
I'm trying to build my own connector for the first time. I'm getting the following error:
Sorry, we failed to submit your credentials because there was an error in the connector in processing the credentials.
Here is my code so far:
function getAuthType() {
var cc = DataStudioApp.createCommunityConnector();
return cc.newAuthTypeResponse()
.setAuthType(cc.AuthType.USER_TOKEN)
.setHelpUrl('https://api-doc-help-url/authentication')
.build();
}
function validateCredentials(userName, token) {
var rawResponse = UrlFetchApp.fetch('https://api/v2/stuff/' + userName , {
method: 'GET',
headers: {
'Authorization': userName + ':' + token
},
muteHttpExceptions: true
});
return rawResponse.getResponseCode() === 200;
}
function isAuthValid() {
var userProperties = PropertiesService.getUserProperties();
var userName = userProperties.getProperty('dscc.username');
var token = userProperties.getProperty('dscc.token');
return validateCredentials(userName, token);
}
function setCredentials(request) {
var creds = request.userToken;
var username = creds.username;
var token = creds.token;
var validCreds = checkForValidCreds(username, token);
if (!validCreds) {
return {
errorCode: 'INVALID_CREDENTIALS'
};
}
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('dscc.username', username);
userProperties.setProperty('dscc.token', token);
return {
errorCode: 'NONE'
};
}
I'm not sure how all this work exactly. I know that I need to pass the Authorization in the header with the following value userName:token
When deploying my manifest and clicking the Google Data Studio link I'm redirected to the select connector data studio interface. The error appear when I'm trying to submit Usernam and Token.
checkForValidCreds is not defined inside the function setCredentials. which is causing the error
I am trying to do authentication using identity server 4 for my react app.i followed this documentation.I am using implicit flow of identity server so onload application it will go to login page of identity server. after giving proper username and password it will validate and give a token.Everything is working as expected but i am not able to redirect my react app to Dashboard page.I am very new to react please help me.
// Copyright (c) Microsoft. All rights reserved.
import Config from 'app.config';
import AuthenticationContext from 'adal-angular/dist/adal.min.js'
import { Observable } from 'rxjs';
import { HttpClient } from 'utilities/httpClient';
import { toUserModel, authDisabledUser } from './models';
import Oidc, { User } from 'oidc-client';
const ENDPOINT = Config.serviceUrls.auth;
export class AuthService {
//static authContext; // Created on AuthService.initialize()
//static authEnabled = true;
//static aadInstance = '';
//static appId = '00000000-0000-0000-0000-000000000000';
//static tenantId = '00000000-0000-0000-0000-000000000000';
//static clientId = '00000000-0000-0000-0000-000000000000';
static initialize() {
if (typeof global.DeploymentConfig === 'undefined') {
alert('The dashboard configuration is missing.\n\nVerify the content of webui-config.js.');
throw new Error('The global configuration is missing. Verify the content of webui-config.js.');
}
if (typeof global.DeploymentConfig.authEnabled !== 'undefined') {
AuthService.authEnabled = global.DeploymentConfig.authEnabled;
if (!AuthService.authEnabled) {
console.warn('Auth is disabled! (see webui-config.js)');
}
}
//AuthService.tenantId = global.DeploymentConfig.aad.tenant;
//AuthService.clientId = global.DeploymentConfig.aad.appId;
//AuthService.appId = global.DeploymentConfig.aad.appId;
//AuthService.aadInstance = global.DeploymentConfig.aad.instance;
if (AuthService.aadInstance && AuthService.aadInstance.endsWith('{0}')) {
AuthService.aadInstance = AuthService.aadInstance.substr(0, AuthService.aadInstance.length - 3);
}
// TODO: support multiple types/providers
if (AuthService.isEnabled() && global.DeploymentConfig.authType !== 'aad') {
throw new Error(`Unknown auth type: ${global.DeploymentConfig.authType}`);
}
//AuthService.authContext = new AuthenticationContext({
// instance: AuthService.aadInstance,
//tenant: AuthService.tenantId,
//clientId: AuthService.clientId,
//redirectUri: "http://localhost:3000/dashboard",
//expireOffsetSeconds: 300, // default is 120
//postLogoutRedirectUri: window.location.protocol
//});
}
static isDisabled() {
return AuthService.authEnabled === false;
}
static isEnabled() {
return !AuthService.isDisabled();
}
static onLoad(successCallback) {
debugger;
AuthService.initialize();
if (AuthService.isDisabled()) {
console.debug('Skipping Auth onLoad because Auth is disabled');
if (successCallback) successCallback();
return;
};
var config = {
authority: "http://localhost:5000",
client_id: "mvc",
redirect_uri: "http://localhost:3000/dashboard",
response_type: "id_token token",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);
mgr.signinRedirect();
mgr.getUser().then(function(user){
if(user){
console.log("User logged in", user.profile);
}
else {
console.log("User not logged in");
}
});
mgr.events.addUserLoaded(function(userLoaded){
mgr.User=userLoaded;
})
mgr.events.addSilentRenewError(function (error){
console.log('the user has signrd out');
mgr._user=null;
})
//mgr.login();
//mgr.renewToken();
// Note: "window.location.hash" is the anchor part attached by
// the Identity Provider when redirecting the user after
// a successful authentication.
// if (AuthService.authContext.isCallback(window.location.hash)) {
// console.debug('Handling Auth Window callback');
// // Handle redirect after authentication
// AuthService.authContext.handleWindowCallback();
// const error = AuthService.authContext.getLoginError();
// if (error) {
// throw new Error(`Authentication Error: ${error}`);
// }
// } else {
// AuthService.getUserName(user => {
// if (user) {
// console.log(`Signed in as ${user.Name} with ${user.Email}`);
// if (successCallback) successCallback();
// } else {
// console.log('The user is not signed in');
// AuthService.authContext.login();
// }
// });
// }
}
static getUserName(callback) {
if (AuthService.isDisabled()) return;
if (AuthService.authContext.getCachedUser()) {
Observable.of({ Name:AuthService.authContext._user.userName, Email: AuthService.authContext._user.userName })
.map(data => data ? { Name: data.Name, Email: data.Email } : null)
.subscribe(callback);
} else {
console.log('The user is not signed in');
AuthService.authContext.login();
}
}
/** Returns a the current user */
static getCurrentUser() {
if (AuthService.isDisabled()) {
return Observable.of(authDisabledUser);
}
return HttpClient.get(`${ENDPOINT}users/current`)
.map(toUserModel);
}
static logout() {
if (AuthService.isDisabled()) return;
AuthService.authContext.logOut();
AuthService.authContext.clearCache();
}
/**
* Acquires token from the cache if it is not expired.
* Otherwise sends request to AAD to obtain a new token.
*/
static getAccessToken() {
debugger;
if (AuthService.isDisabled()) {
return Observable.of('client-auth-disabled');
}
return Observable.create(observer => {
return AuthService.authContext.acquireToken(
AuthService.appId,
(error, accessToken) => {
if (error) {
console.log(`Authentication Error: ${error}`);
observer.error(error);
}
else observer.next(accessToken);
observer.complete();
}
);
});
}
}
The problem i am facing is after authentication my app is going some kind of loop means the url of app is changing to identity server and local app url.you can see my app was using AuthenticationContext from adal previously.i want to change into oidc for identity server4.
I see that you mentioned redirect uri as dashboard? 'redirect_uri: "http://localhost:3000/dashboard'. So from the Identity server, the user will be redirected here right? Can you show us what you are doing on the dashboard page?
Typically, in Identity server implementation, redirect Uri needs to be a simple page whose responsibility needs to be nothing but accessing the Tokens from the URL and redirecting to the desired page (like redirecting to the dashboard from here)
I understand what I gave you is more of a theoretical answer, but seeing your implementation of redirect URL help you get a better answer.