I'm trying to update my .NET Core 3.0 React SPA to use code flow as opposed to implicit.
It is failing with "Invalid authorization code" in the logs of Identity Server.
Can anyone tell what is going wrong or what to check / try?
Do i need to do anything for PKCE? or just set it to true? (RequirePkce = true)
Seems to get a fair way before erroring.
I'm using oidc-client NPM package on the front end.
[13:14:44 Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.DiscoveryEndpoint" for "/.well-known/openid-configuration" (IdentityServer4.Hosting.IdentityServerMiddleware)
[13:14:44 Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.TokenEndpoint" for "/connect/token" (IdentityServer4.Hosting.IdentityServerMiddleware)
[13:14:44 Information] ClientAuthenticationSuccessEvent { ClientId: "MyProject.web", AuthenticationMethod: "NoSecret", Category: "Authentication", Name: "Client Authentication Success", EventType: Success, Id: 1010, Message: null, ActivityId: "80000050-0007-fe00-b63f-84710c7967bb", TimeStamp: 09/13/2019 03:14:44, ProcessId: 19196, LocalIpAddress: "::1:44343", RemoteIpAddress: "::1" } (IdentityServer4.Events.DefaultEventService)
[13:14:44 Information] Token request validation success
TokenRequestValidationLog { ClientId: "MyProject.web", ClientName: "MyProject.web", GrantType: "authorization_code", Scopes: null, AuthorizationCode: "d473eae4ba0ca70d14ac02b1907466067ae97847cdba5f46ba78ce6a51d4c171", RefreshToken: null, UserName: null, AuthenticationContextReferenceClasses: null, Tenant: null, IdP: null, Raw: [("client_id": "MyProject.web"), ("code": "d473eae4ba0ca70d14ac02b1907466067ae97847cdba5f46ba78ce6a51d4c171"), ("redirect_uri": "https://localhost:44343/authentication/login-callback"), ("code_verifier": "7103488868084ec4aa94a62bcb9b422eac6fc24203eb4b14a8fdc9f3cad9839c358780cc40c546ecb8d58ac5e118b63e"), ("grant_type": "authorization_code")] } (IdentityServer4.Validation.TokenRequestValidator)
[13:14:44 Information] TokenIssuedSuccessEvent { ClientId: "MyProject.web", ClientName: "MyProject.web", RedirectUri: null, Endpoint: "Token", SubjectId: null, Scopes: "openid profile MyProject.webAPI", GrantType: "authorization_code", Tokens: [Token { TokenType: "id_token", TokenValue: "****gPHA" }, Token { TokenType: "access_token", TokenValue: "****YH5A" }], Category: "Token", Name: "Token Issued Success", EventType: Success, Id: 2000, Message: null, ActivityId: "80000050-0007-fe00-b63f-84710c7967bb", TimeStamp: 09/13/2019 03:14:44, ProcessId: 19196, LocalIpAddress: "::1:44343", RemoteIpAddress: "::1" } (IdentityServer4.Events.DefaultEventService)
[13:14:44 Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.UserInfoEndpoint" for "/connect/userinfo" (IdentityServer4.Hosting.IdentityServerMiddleware)
[13:14:44 Information] Profile service returned the following claim types: "given_name family_name"
(IdentityServer4.ResponseHandling.UserInfoResponseGenerator)
[13:14:44 Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.TokenEndpoint" for "/connect/token" (IdentityServer4.Hosting.IdentityServerMiddleware)
[13:14:44 Information] ClientAuthenticationSuccessEvent { ClientId: "MyProject.web", AuthenticationMethod: "NoSecret", Category: "Authentication", Name: "Client Authentication Success", EventType: Success, Id: 1010, Message: null, ActivityId: "8000000c-0002-fc00-b63f-84710c7967bb", TimeStamp: 09/13/2019
03:14:44, ProcessId: 19196, LocalIpAddress: "::1:44343", RemoteIpAddress: "::1" } (IdentityServer4.Events.DefaultEventService)
[13:14:44 Error] Invalid authorization code{ code: "d473eae4ba0ca70d14ac02b1907466067ae97847cdba5f46ba78ce6a51d4c171" }, details: TokenRequestValidationLog { ClientId: "MyProject.web", ClientName: "MyProject.web", GrantType: "authorization_code", Scopes: null, AuthorizationCode: "d473eae4ba0ca70d14ac02b1907466067ae97847cdba5f46ba78ce6a51d4c171", RefreshToken: null, UserName: null, AuthenticationContextReferenceClasses: null, Tenant: null, IdP: null, Raw: [("client_id": "MyProject.web"), ("code": "d473eae4ba0ca70d14ac02b1907466067ae97847cdba5f46ba78ce6a51d4c171"), ("redirect_uri": "https://localhost:44343/authentication/login-callback"), ("code_verifier": "7103488868084ec4aa94a62bcb9b422eac6fc24203eb4b14a8fdc9f3cad9839c358780cc40c546ecb8d58ac5e118b63e"), ("grant_type": "authorization_code")] }
(IdentityServer4.Validation.TokenRequestValidator)
[13:14:44 Information] TokenIssuedFailureEvent { ClientId: "MyProject.web", ClientName: "MyProject.web", RedirectUri: null, Endpoint: "Token", SubjectId: null, Scopes: null, GrantType: "authorization_code", Error: "invalid_grant", ErrorDescription: null, Category: "Token", Name: "Token Issued Failure", EventType: Failure, Id: 2001, Message: null, ActivityId: "8000000c-0002-fc00-b63f-84710c7967bb", TimeStamp: 09/13/2019 03:14:44, ProcessId: 19196, LocalIpAddress: "::1:44343", RemoteIpAddress: "::1" } (IdentityServer4.Events.DefaultEventService)
Below is an implementation of Authorization Code Flow with Identity Server 4
public class Example
{
public static IEnumerable<Test> Get()
{
var shakey = new Secret { Value = "mysecret".Sha512() };
return new List<Test> {
new Test {
TestId = "authorizationCodeTest2",
TestName = "Authorization Code Test",
TestSecrets = new List<Secret> { shakey },
Enabled = true,
AllowedGrantTypes = new List<string> { "authorization_code" },
AllowRememberConsent = false,
RequireConsent = true,
RedirectUris =
new List<string> {
"http://localhost:<<port>>/account/oAuth2"
},
PostLogoutRedirectUris =
new List<string> {"http://localhost:<<port>>"},
AllowedScopes = new List<string> {
"api"
},
AccessTokenType = AccessTokenType.Jwt
}
};
}
}
Check if you are missing something on the Authentication Token and retry.
Related
I am trying to use next-auth with my backend but it doesn't work. I use version 4 with typescript. The error is
{error: 'window is not defined', status: 200, ok: true, url: null}
Why?????. Thanks a lot.
My custom API /login result is
{
"data": {
"username": "test",
"users": {
"id": 2,
"username": "test",
"email": "test#test.com",
"createdAt": "2021-05-24",
"updatedAt": "2021-05-24",
"name": "John Smith",
"id_groups": 99,
"groups": "guest",
"avatar": null
},
"timestamp": 1646808511,
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiG9.eyJpc3MiOiJodHRwOlwvXC90d2luYXBwLml0IiwiYXVkIjoiaHR0cDpcL1wvdHdpbmFwcC5pdCIsImlhdCI6MTM1Njk5OTUyNCwibmJmIjoxMzU3MDAwMDAwLCJleHAiOjE2NDY4MTIxMTEsImRhdGEiOiJtYXJjb2JvbmNpIn0.R1aAX99GHmoSPRKv4Vnzso8iRjUhrDWhPEdq4oql_r0"
},
"status": "",
"code": 200
}
Now, I'm try to configure next auth
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import gApi from "../../../api/gApi";
export default NextAuth({
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {label: "Username",type: "text", placeholder: "username"},
password: { label: "Passwort", type: "password" },
},
async authorize(credentials) {
const resp = await gApi.post("/login", JSON.stringify(credentials));
const user = resp.data;
console.log('CALL MY API');
console.log(resp);
if ( resp.status && user) {
return user;
}
return null;
},
}),
],
callbacks: {
async jwt({ token, user, account, isNewUser }) {
if (user) {
if (user.jwt) {
token = { accessToken: user.jwt };
}
}
return token;
},
async session({ session, token }) { // this token return above jwt()
session.accessToken = token.accessToken;
return session;
},
},
pages: {
signIn: "/auth/Login",
},
});
In my login page I have e simple form and i call with:
const onSubmit: SubmitHandler<FormData> = async data => {
const resp: any = await signIn("credentials", {
username: data.username,
password: data.password,
redirect: false,
});
console.log('RESPO signin');
console.log(resp);
if (resp && !resp.error) {
router.replace('/')
} else return;
}
Using login with Microsoft on Azure AD B2C I get the following error:
invalid_request: The provided value for the input parameter 'redirect_uri' is not valid. The expected value is a URI which matches a redirect URI registered for this client application.
I can reach other providers and login with email just not Microsoft.. lol.
I have searched for hours and tried everything I can think of, hopefully someone else can help identify the issue. Initially I was only able to get Microsoft login to work using https://login.microsoft.com/common or something similar but that did not use my userflow/ allow other providers. Now that I have the userflow working from my application I cannot login with Microsoft. Below is my config and code.
I initially followed the Microsoft tutorial here:
https://learn.microsoft.com/en-us/azure/developer/javascript/tutorial/single-page-application-azure-login-button-sdk-msal
then pieced others together to get it to use my userflow to execute and it works other than login with Microsoft.
Registered Application Manifest on Azure:
{
"id": "<ID>",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": true,
"appId": "<app id>",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2021-06-09T22:15:39Z",
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "Management",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"preAuthorizedApplications": [],
"publisherDomain": "dwsdevb2c.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://jwt.ms/",
"type": "Spa"
},
{
"url": "https://jwt.ms",
"type": "Spa"
},
{
"url": "http://localhost:3000/",
"type": "Spa"
},
{
"url": "http://localhost:3000",
"type": "Spa"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": "http://localhost:3000/",
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [
"notApiConsumer",
"singlePageApp"
],
"tokenEncryptionKeyId": null
}
azure-authentication-config.tsx
import { Configuration, LogLevel } from '#azure/msal-browser';
const AzureActiveDirectoryAppClientId: any =
process.env.REACT_APP_AZURE_ACTIVE_DIRECTORY_APP_CLIENT_ID;
export const b2cPolicies = {
names: {
signUpSignIn: 'B2C_1_dwsdevuserflow01',
forgotPassword: 'B2C_1_dwsdevuserflow01',
editProfile: 'B2C_1_dwsdevprofileflow01',
},
authorities: {
signUpSignIn: {
authority:
'https://dwsdevb2c.b2clogin.com/dwsdevb2c.onmicrosoft.com/B2C_1_dwsdevuserflow01',
},
forgotPassword: {
authority:
'https://dwsdevb2c.b2clogin.com/dwsdevb2c.onmicrosoft.com/B2C_1_dwsdevuserflow01',
},
editProfile: {
authority:
'https://dwsdevb2c.b2clogin.com/dwsdevb2c.onmicrosoft.com/B2C_1_dwsdevprofileflow01',
},
},
authorityDomain: 'https://dwsdevb2c.b2clogin.com',
// authorityDomain: 'https://login.microsoft.com/common',
};
export const MSAL_CONFIG: Configuration = {
auth: {
clientId: AzureActiveDirectoryAppClientId,
authority: b2cPolicies.authorities.signUpSignIn.authority,
knownAuthorities: [b2cPolicies.authorityDomain],
redirectUri: window.location.origin,
postLogoutRedirectUri: window.location.origin, // Indicates the page to navigate after logout.
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: true,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.error(message);
return;
case LogLevel.Verbose:
console.error(message);
return;
case LogLevel.Warning:
console.error(message);
return;
default:
break;
}
},
},
},
};
azure-authentication-context.tsx
import {
PublicClientApplication,
AuthenticationResult,
AccountInfo,
EndSessionRequest,
RedirectRequest,
PopupRequest,
} from '#azure/msal-browser';
import { MSAL_CONFIG } from './azure-authentication-config';
export class AzureAuthenticationContext {
private myMSALObj: PublicClientApplication = new PublicClientApplication(
MSAL_CONFIG,
);
private account?: AccountInfo;
private loginRedirectRequest?: RedirectRequest;
private loginRequest?: PopupRequest;
public isAuthenticationConfigured = false;
constructor() {
// #ts-ignore
this.account = null;
this.setRequestObjects();
if (MSAL_CONFIG?.auth?.clientId) {
this.isAuthenticationConfigured = true;
}
}
private setRequestObjects(): void {
this.loginRequest = {
scopes: ['openid', 'profile'],
prompt: 'select_account',
};
this.loginRedirectRequest = {
...this.loginRequest,
redirectStartPage: MSAL_CONFIG.auth.redirectUri, //window.location.href,
};
}
login(signInType: string, setUser: any): void {
if (signInType === 'loginPopup') {
this.myMSALObj
.loginPopup(this.loginRequest)
.then((resp: AuthenticationResult) => {
this.handleResponse(resp, setUser);
})
.catch((err) => {
console.error(err);
});
} else if (signInType === 'loginRedirect') {
this.myMSALObj.loginRedirect(this.loginRedirectRequest);
}
}
logout(account: AccountInfo): void {
const logOutRequest: EndSessionRequest = {
account,
};
this.myMSALObj.logout(logOutRequest);
}
handleResponse(response: AuthenticationResult, incomingFunction: any) {
if (response !== null && response.account !== null) {
this.account = response.account;
} else {
this.account = this.getAccount();
}
if (this.account) {
incomingFunction(this.account);
}
}
private getAccount(): AccountInfo | undefined {
console.log(`loadAuthModule`);
const currentAccounts = this.myMSALObj.getAllAccounts();
if (currentAccounts === null) {
// #ts-ignore
console.log('No accounts detected');
return undefined;
}
if (currentAccounts.length > 1) {
// #ts-ignore
console.log(
'Multiple accounts detected, need to add choose account code.',
);
return currentAccounts[0];
} else if (currentAccounts.length === 1) {
return currentAccounts[0];
}
}
}
export default AzureAuthenticationContext;
In AzureAD navigate to Home => App Registrations > YOUR_APP
Under “Single-page application” you should see the Redirect URIs listed. It is my understanding that the redirectUri value under Auth in your MSAL_CONFIG file needs to match on of the URI’s listed there. Have you confirmed that is the case? I am unable to tell what ‘window.location.origin’ is producing based on your config.
I have a MVC client accessing a web API protected by IDS4 server. It works perfectly when I use ResponseType = "code". But when I change it to ResponseType = "code id_token" (hybrid flow), I start to get this "Sorry, there was an error : invalid_request code challenge required" error.
Here is my client configuration on the IDS4 side:
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequirePkce = false,
//AllowedGrantTypes = GrantTypes.Code,
RedirectUris = { "https://localhost:6009/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:6009/signout-callback-oidc" },
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"roles",
"MyAPI"
}
And here is the configuration on the MVC Client side. Note that it is the 'options.ResponseType = "code id_token"' that made the error occur. It will not error if I use "code" instead.
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:6005";
options.ClientId = "mvc";
options.ClientSecret = "secret";
//options.ResponseType = "code";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.Scope.Add("profile");
options.Scope.Add("email");
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("roles");
options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.RoleClaimType = "role";
options.Scope.Add("MyAPI");
options.Scope.Add("offline_access");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
};
});
And here is what's in the log:
[2021-03-12T15:25:51.014Z] [4] [Error] Request validation failed
[2021-03-12T15:25:51.013Z] [4] [Error] code_challenge is missing
{
"ClientId": "mvc",
"RedirectUri": "https://localhost:6009/signin-oidc",
"AllowedRedirectUris": [
"https://localhost:6009/signin-oidc"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "fragment",
"GrantType": "hybrid",
"RequestedScopes": "",
"State": "CfDJ....8oxh4",
"PromptMode": "",
"Raw": {
"client_id": "mvc",
"redirect_uri": "https://localhost:6009/signin-oidc",
"response_type": "code id_token",
"scope": "openid profile email roles MyAPIoffline_access",
"response_mode": "form_post",
"nonce": "637511...lOGNh",
"state": "CfDJ8...8oxh4",
"x-client-SKU": "ID_NETSTANDARD2_0",
"x-client-ver": "5.5.0.0"
}
}
By the way, I am testing hybrid flow because I am trying to make the Windows authentication working under IDS4. My local login works but not Windows. Some resource says that hybrid flow is needed for that. Can somebody tell me what I am doing wrong here?
It could be that IdentityServer always requires PKCE and that's why it complains when you don't provide the necessary challenge.
Dominic writes here:
Code flow without PKCE is vulnerable to code injection attacks. That's why we do not expose it.
Adding the code challenge/verifier in a client is not that complicated.
Here's how I present OAuth 2.1 in my training classes:
I am trying to develop a word add in in React with Typescript that allows me to access the dynamics 365 Web Api. The best example I've actually found was in a sample excel add in that accesses the graph api using MSAL 2.0 here https://github.com/OfficeDev/PnP-OfficeAddins/tree/master/Samples/auth/Office-Add-in-Microsoft-Graph-React. If I can get a successful get request to my Dynamics 365 api after modifying the example code I will port the code over to my Word Add in.
However I keep getting a 401 error when trying to go to https://saltrial.crm.dynamics.com/api/data/v9.1/
I get a response back when I simply paste this in a browser, but inside the add in I get 401 unauthorized. I also get 401 unauthorized when I paste the token received from the add in into postman with the header of Bearer + token. I have gotten a successful Access token however when I selected
I will show you my setup in Azure AD.... I have a client secret setup, but am not using it in my add in code.
Manifest
{
"id": "35c3a758-0edb-45f6-a97d-9c7180decd73",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": true,
"appId": "4f7xxxxxxxxxxxxxxxxxxxxxx310bf0",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2021-01-24T07:09:12Z",
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [
"api://localhost:3000/4fxxxxxxxxxxxxxxxxxxxxxxx"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "TrySSO",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [
{
"adminConsentDescription": "Enable Office to call the add-in's web APIs with the same rights as the current user.",
"adminConsentDisplayName": "Office can act as the user",
"id": "5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"type": "User",
"userConsentDescription": "Enable Office to call the add-in's web APIs with the same rights that you have.",
"userConsentDisplayName": "Office can act as you",
"value": "access_as_user"
}
],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-31T05:00:00Z",
"keyId": "570axxxxxxxxxxxxxxxxxxxxxxxxxxbcd1",
"startDate": "2021-01-28T04:11:05.086Z",
"value": null,
"createdOn": "2021-01-28T04:11:06.027737Z",
"hint": "mm-",
"displayName": "wordaddinsecret"
}
],
"preAuthorizedApplications": [
{
"appId": "ea5a67f6-b6f3-4338-b240-c655ddc3cc8e",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "d3590ed6-52b3-4102-aeff-aad2292ab01c",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "57fb890c-0dab-4253-a5e0-7188c88b2bb4",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "bc59ab01-8403-45c6-8796-ac3ef710b3e3",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
}
],
"publisherDomain": "salnewtrial.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://localhost:3000/login/login.html",
"type": "Spa"
},
{
"url": "https://login.microsoftonline.com/common/oauth2/nativeclient",
"type": "InstalledClient"
},
{
"url": "https://localhost:3000/login.html",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000007-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "78ce3f0f-a1ce-49c2-8cde-64b5c0896db4",
"type": "Scope"
}
]
},
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null
}
Inside my login.ts in excel add in
(() => {
// The initialize function must be run each time a new page is loaded
Office.initialize = () => {
const msalInstance = new PublicClientApplication({
auth: {
clientId: "4f7f40ec-xxxxxxxxx-5d6b18310bf0",
authority: "https://login.microsoftonline.com/cd77a053-xxxxxxxxxx3402c0fd62", *This is tenant id
redirectUri: "https://localhost:3000/login/login.html", // Must be registered as "spa" type
},
cache: {
cacheLocation: "localStorage", // needed to avoid "login required" error
storeAuthStateInCookie: true, // recommended to avoid certain IE/Edge issues
},
});
// handleRedirectPromise should be invoked on every page load
msalInstance
.handleRedirectPromise()
.then((response) => {
// If response is non-null, it means page is returning from AAD with a successful response
if (response) {
Office.context.ui.messageParent(
JSON.stringify({ status: "success", result: response.accessToken })
);
} else {
// Otherwise, invoke login
msalInstance.loginRedirect({
scopes: [
"user.read",
"files.read.all",
"https://saltrial.crm.dynamics.com//user_impersonation",
],
});
}
})
.catch((error) => {
const errorData: string = `errorMessage: ${error.errorCode}
message: ${error.errorMessage}
errorCode: ${error.stack}`;
Office.context.ui.messageParent(
JSON.stringify({ status: "failure", result: errorData })
);
});
};
})();
Here is my API Call in Add in
import axios from 'axios';
export const getGraphData = async (url: string, accesstoken: string) => {
const response = await axios({
url: url,
method: 'get',
headers: {'Authorization': `Bearer ${accesstoken}`,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
"Accept": "application/json",
"Content-Type": "application/json; charset=utf-8",
}
});
return response;
};
Code that calls my api function and passes the scopes and dynamics url. I console log the access token and put it into postman with header Bearer token, I get 401. Also the add-in displays 401 unauthorized in the task pane....Ignore any naming of functions referring to the graph api, I'm hitting the dynamics 365 api and hoping I don't get the 401 error. Thanks.
getFileNames = async () => {
this.setState({ fileFetch: "fetchInProcess" });
getGraphData(
// Get the `name` property of the first 3 Excel workbooks in the user's OneDrive.
"https://saltrial.crm.dynamics.com/api/data/v9.1/WhoAmI",
this.accessToken
)
.then(async (response) => {
await writeFileNamesToWorksheet(response, this.displayError);
this.setState({ fileFetch: "fetched", headerMessage: "Success" });
})
.catch((requestError) => {
// If this runs, then the `then` method did not run, so this error must be
// from the Axios request in getGraphData, not the Office.js in
// writeFileNamesToWorksheet
console.log("Access Token >>>>>>>>>>>>>>>>> ", this.accessToken);
this.displayError(requestError);
});
};
I get an invalid grant when I try to login from my WinForms app to IS4.
This is the server log:
fail: IdentityServer4.Validation.TokenRequestValidator[0]
Unexpected code_verifier: 12a783b32873a5b4ae0eb7113a067cd978d3d345a8cb29cc0a1a6df131c5839a
fail: IdentityServer4.Validation.TokenRequestValidator[0]
{
"ClientId": "las",
"ClientName": "LAS.NET Client",
"GrantType": "authorization_code",
"AuthorizationCode": "e301575cc20f47acf7c15178310f776642a7a30cf2b6a05f54702097b1645b7a",
"Raw": {
"grant_type": "authorization_code",
"code": "e301575cc20f47acf7c15178310f776642a7a30cf2b6a05f54702097b1645b7a",
"redirect_uri": "http://localhost/winforms.client",
"code_verifier": "12a783b32873a5b4ae0eb7113a067cd978d3d345a8cb29cc0a1a6df131c5839a",
"client_id": "las",
"client_secret": "secret"
}
}
The LoginResult.Error says "invalid_grant".
This is the client setup:
new Client
{
ClientId = "las",
ClientName = "LAS.NET Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
"api1"
},
RedirectUris = { "http://localhost/winforms.client" },
AllowOfflineAccess = true,
RequireConsent = false
},
and this is how I initialize my winform app:
var options = new OidcClientOptions
{
Authority = "http://localhost:5000",
ClientId = "las",
ClientSecret = "secret",
RedirectUri = "http://localhost/winforms.client",
Scope = "openid profile api1 offline_access",
Browser = new WinFormsEmbeddedBrowser(),
Flow = OidcClientOptions.AuthenticationFlow.Hybrid
};
_oidcClient = new OidcClient(options);
How can I fix this issue?
Your WinForms client is telling IdentityServer that it wants to do PKCE however the client does not look like it requires PKCE. In your client configuration/setup add RequirePkce = true.