Often getting 401 from Directline API within Webchat
See original GitHub issueScreenshots
Version
Latest CDN: https://cdn.botframework.com/botframework-webchat/latest/webchat.js
Describe the bug
In an Angular application we use the bot secret to generate a new token and then pass the token to the webchat. Below are snippets of code:
(Note that some portions are omitted and replaced with ellipsis)
directline-token-provider.ts
Retrieves a secret from our web app and uses that to retrieve a token
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
//Will be modified to hold onto a token in RAM and in cookies
@Injectable({
providedIn: 'root',
})
export class DirectlineTokenProvider {
constructor(private httpClient: HttpClient) { }
getDirectLineToken(conversationId: string, continueConversation: boolean) {
//MODIFY THIS URL TO TARGET A REMOTE SOURCE
return this.httpClient.get<DirectLineTokenResponse>('/api/configuration/directlinetoken').toPromise()
.then(r => {
const directlineSecret = r.directLineToken;
console.log(`Requested continue conversation: ${!!continueConversation}`);
if (!!continueConversation && !!conversationId) {
console.log(`Continuing Conversation ID: ${conversationId}`);
return this.continueConversation(directlineSecret, conversationId);
}
console.log('Generating new token for new conversation');
return this.generateNewToken(directlineSecret);
});
}
private continueConversation(directlineSecret: string, conversationId: string) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${directlineSecret}`
})
};
return this.httpClient.get<any>(`https://directline.botframework.com/v3/directline/conversations/${conversationId}`, httpOptions).toPromise()
.then(r => r.token);
}
private generateNewToken(directlineSecret: string) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${directlineSecret}`
})
};
return this.httpClient.post<any>('https://directline.botframework.com/v3/directline/tokens/generate', null, httpOptions).toPromise()
.then(r => r.token);
}
}
export class DirectLineTokenResponse {
directLineToken: string;
}
We do seem to be getting a valid token via the _https://directline.botframework.com/v3/directline/tokens/generate_ endpoint.
###We then consume the token in a component here:
ngOnInit() {
...
var params = new URLSearchParams(window.location.search);
var continueConversation = params.get("continueConversation") === 'true';
var conversationId = this.getCookie("conversationId");
this.userInfoProvider.getUserInfoAsync().then((portalUser: string | PortalUser) => {
this.portalUser = portalUser;
return this.directlineTokenProvider.getDirectLineToken(conversationId, continueConversation);
}).then(token => {
console.log(`Using token: ${token}`)
this.createConnection(token);
});
...
}
Here is “createConnection”:
createConnection(directLineToken: string) {
var directLine = this.botWebChat.createDirectLine({ token: directLineToken });
...
const store = this.botWebChat.createStore(
{},
({ dispatch }) => next => action => {
// This switch statement works. considered removing it from this post but left it for context
switch (action.type) {
case 'WEB_CHAT/SET_SUGGESTED_ACTIONS':
this.addHoverText();
break;
case 'DIRECT_LINE/INCOMING_ACTIVITY':
if (action.payload.activity.type === 'X_FeedbackRequest') {
this.feedbackRequestObservable.next(action.payload);
}
break;
case 'DIRECT_LINE/POST_ACTIVITY':
action = window.simpleUpdateIn(action, ['payload', 'activity', 'channelData', 'PortalUser'], () => this.portalUser);
break;
default:
break;
}
return next(action);
}
);
this.botWebChat.renderWebChat(
{
directLine: Object.assign({}, directLine, {
postActivity: function (activity) {
activity.channelData.hostUrl = window.location.href;
return directLine.postActivity(activity);
}
}),
styleSet: styleSet,
store
},
this.webchat.nativeElement
);
this.botWebChat["directLine"] = directLine;
...
}
Randomly we get a 401 unauthorized response from directline when posting messages. Maybe I am missing something, but I can’t tell of a race condition where the webchat is initialized before the token is ready. This was my first instinct.
Any thoughts? We also have this issue with the following non-angular code: (Uses the actual direct line secret without negotiating for a token)
function createConnection(directLineSecret) {
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
...
return next(action);
});
window.WebChat.renderWebChat(
{
directLine: window.WebChat.createDirectLine({ token: directLineSecret}),
styleSet: styleSet,
store
},
document.getElementById('webchat')
);
...
}
Issue Analytics
- State:
- Created 4 years ago
- Comments:12 (8 by maintainers)
Top GitHub Comments
@JIoffe I was working with engineers and we are pretty sure this is the cause:
directline.botframework.com
directline.botframework.com
, when it receive “create conversation” call, it will contact the bot with aconversationUpdate
activitydirectline.botframework.com
will response to step 1 with HTTP 401, instead of the usual 502 Gateway TimeoutWe looked at our server log for 401 with the conversation ID you referenced. The bot timed out on step 2, when we send
conversationUpdate
activity. Thus,directline.botframework.com
returned 401.As the root cause has been identified, I am closing this issue for now.
Thanks @EricDahlvang and @p-nagpal for server-side debugging. 👍🏻
A fix was deployed world wide for this last week. Please let me know if you are still seeing this issue.