Skip to content

Commit 7d9ef67

Browse files
committed
Completed add AAD auth step
1 parent 9f6ecfd commit 7d9ef67

File tree

5 files changed

+224
-209
lines changed

5 files changed

+224
-209
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@ typings/
6464
.vscode/
6565

6666
Config.js
67+
Config.ts
6768
ReactQuickStart.zip

demo/graph-tutorial/src/App.tsx

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import React, { Component } from 'react';
44
import { BrowserRouter as Router, Route } from 'react-router-dom';
55
import { Container } from 'reactstrap';
6+
import { UserAgentApplication } from 'msal';
67
import NavBar from './NavBar';
78
import ErrorMessage from './ErrorMessage';
89
import Welcome from './Welcome';
10+
import { config } from './Config';
11+
import { getUserDetails } from './GraphService';
912
import 'bootstrap/dist/css/bootstrap.css';
1013

1114
interface AppState {
@@ -15,28 +18,51 @@ interface AppState {
1518
}
1619

1720
class App extends Component<any, AppState> {
21+
private userAgentApplication: UserAgentApplication;
22+
23+
// <constructorSnippet>
1824
constructor(props: any) {
1925
super(props);
2026

27+
this.userAgentApplication = new UserAgentApplication({
28+
auth: {
29+
clientId: config.appId,
30+
redirectUri: config.redirectUri
31+
},
32+
cache: {
33+
cacheLocation: "localStorage",
34+
storeAuthStateInCookie: true
35+
}
36+
});
37+
38+
var account = this.userAgentApplication.getAccount();
39+
2140
this.state = {
22-
isAuthenticated: false,
41+
isAuthenticated: (account !== null),
2342
user: {},
2443
error: null
2544
};
45+
46+
if (account) {
47+
// Enhance user object with data from Graph
48+
this.getUserProfile();
49+
}
2650
}
51+
// </constructorSnippet>
2752

2853
render() {
2954
let error = null;
3055
if (this.state.error) {
3156
error = <ErrorMessage message={this.state.error.message} debug={this.state.error.debug} />;
3257
}
3358

59+
// <renderSnippet>
3460
return (
3561
<Router>
3662
<div>
3763
<NavBar
3864
isAuthenticated={this.state.isAuthenticated}
39-
authButtonMethod={null}
65+
authButtonMethod={this.state.isAuthenticated ? this.logout.bind(this) : this.login.bind(this)}
4066
user={this.state.user}/>
4167
<Container>
4268
{error}
@@ -45,19 +71,106 @@ class App extends Component<any, AppState> {
4571
<Welcome {...props}
4672
isAuthenticated={this.state.isAuthenticated}
4773
user={this.state.user}
48-
authButtonMethod={null} />
74+
authButtonMethod={this.login.bind(this)} />
4975
} />
5076
</Container>
5177
</div>
5278
</Router>
5379
);
80+
// </renderSnippet>
5481
}
5582

5683
setErrorMessage(message: string, debug: string) {
5784
this.setState({
5885
error: {message: message, debug: debug}
5986
});
6087
}
88+
89+
// <loginSnippet>
90+
async login() {
91+
try {
92+
await this.userAgentApplication.loginPopup(
93+
{
94+
scopes: config.scopes,
95+
prompt: "select_account"
96+
});
97+
await this.getUserProfile();
98+
}
99+
catch(err) {
100+
var error = {};
101+
102+
if (typeof(err) === 'string') {
103+
var errParts = err.split('|');
104+
error = errParts.length > 1 ?
105+
{ message: errParts[1], debug: errParts[0] } :
106+
{ message: err };
107+
} else {
108+
error = {
109+
message: err.message,
110+
debug: JSON.stringify(err)
111+
};
112+
}
113+
114+
this.setState({
115+
isAuthenticated: false,
116+
user: {},
117+
error: error
118+
});
119+
}
120+
}
121+
// </loginSnippet>
122+
123+
// <logoutSnippet>
124+
logout() {
125+
this.userAgentApplication.logout();
126+
}
127+
// </logoutSnippet>
128+
129+
async getUserProfile() {
130+
try {
131+
// Get the access token silently
132+
// If the cache contains a non-expired token, this function
133+
// will just return the cached token. Otherwise, it will
134+
// make a request to the Azure OAuth endpoint to get a token
135+
136+
var accessToken = await this.userAgentApplication.acquireTokenSilent({
137+
scopes: config.scopes
138+
});
139+
140+
if (accessToken) {
141+
// Get the user's profile from Graph
142+
var user = await getUserDetails(accessToken.accessToken);
143+
this.setState({
144+
isAuthenticated: true,
145+
user: {
146+
displayName: user.displayName,
147+
email: user.mail || user.userPrincipalName
148+
},
149+
error: null
150+
});
151+
}
152+
}
153+
catch(err) {
154+
var error = {};
155+
if (typeof(err) === 'string') {
156+
var errParts = err.split('|');
157+
error = errParts.length > 1 ?
158+
{ message: errParts[1], debug: errParts[0] } :
159+
{ message: err };
160+
} else {
161+
error = {
162+
message: err.message,
163+
debug: JSON.stringify(err)
164+
};
165+
}
166+
167+
this.setState({
168+
isAuthenticated: false,
169+
user: {},
170+
error: error
171+
});
172+
}
173+
}
61174
}
62175

63176
export default App;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const config = {
2+
appId: 'YOUR_APP_ID_HERE',
3+
redirectUri: 'http://localhost:3000',
4+
scopes: [
5+
'user.read',
6+
'calendars.read'
7+
]
8+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// <graphServiceSnippet1>
5+
var graph = require('@microsoft/microsoft-graph-client');
6+
7+
function getAuthenticatedClient(accessToken: string) {
8+
// Initialize Graph client
9+
const client = graph.Client.init({
10+
// Use the provided access token to authenticate
11+
// requests
12+
authProvider: (done: any) => {
13+
done(null, accessToken);
14+
}
15+
});
16+
17+
return client;
18+
}
19+
20+
export async function getUserDetails(accessToken: string) {
21+
const client = getAuthenticatedClient(accessToken);
22+
23+
const user = await client.api('/me').get();
24+
return user;
25+
}
26+
// </graphServiceSnippet1>

0 commit comments

Comments
 (0)