Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit 3f70c86

Browse files
committed
Allow user to select login strategy
1 parent d7c8452 commit 3f70c86

File tree

8 files changed

+73
-50
lines changed

8 files changed

+73
-50
lines changed

src/Authentication/AuthenticationServiceFactory.cs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,48 @@
88
namespace Microsoft.Graph.Cli.Authentication;
99

1010
class AuthenticationServiceFactory {
11-
public async Task<IAuthenticationService> GetAuthenticationServiceAsync(AuthenticationStrategy strategy, bool persistToken = false) {
11+
public async Task<ILoginService> GetAuthenticationServiceAsync(AuthenticationStrategy strategy) {
1212
switch (strategy) {
1313
case AuthenticationStrategy.DeviceCode:
14-
return await GetDeviceCodeAuthenticationServiceAsync(persistToken);
14+
return await GetDeviceCodeLoginServiceAsync();
1515
default:
1616
throw new InvalidOperationException($"The authentication strategy {strategy} is not supported");
1717
}
1818

1919
}
2020

21-
public async Task<TokenCredential> GetTokenCredentialAsync(AuthenticationStrategy strategy, bool persistToken = false) {
21+
public async Task<TokenCredential> GetTokenCredentialAsync(AuthenticationStrategy strategy) {
2222
switch (strategy) {
2323
case AuthenticationStrategy.DeviceCode:
24-
return await GetDeviceCodeCredentialAsync(persistToken);
24+
return await GetDeviceCodeCredentialAsync();
2525
default:
2626
throw new InvalidOperationException($"The authentication strategy {strategy} is not supported");
2727
}
2828
}
2929

30-
private async Task<DeviceCodeAuthenticationService> GetDeviceCodeAuthenticationServiceAsync(bool persistToken = false) {
31-
var credential = await GetDeviceCodeCredentialAsync(persistToken);
30+
private async Task<DeviceCodeLoginService> GetDeviceCodeLoginServiceAsync() {
31+
var credential = await GetDeviceCodeCredentialAsync();
3232
return new(credential);
3333
}
3434

35-
private async Task<DeviceCodeCredential> GetDeviceCodeCredentialAsync(bool persistToken) {
35+
private async Task<DeviceCodeCredential> GetDeviceCodeCredentialAsync() {
3636
DeviceCodeCredentialOptions credOptions = new()
3737
{
3838
ClientId = Constants.ClientId,
3939
TenantId = Constants.TenantId
4040
};
4141

42-
if (persistToken) {
43-
TokenCachePersistenceOptions tokenCacheOptions = new() { Name = Constants.TokenCacheName };
44-
credOptions.TokenCachePersistenceOptions = tokenCacheOptions;
45-
var recordPath = Constants.AuthRecordPath;
46-
47-
if (File.Exists(recordPath))
48-
{
49-
using var authRecordStream = new FileStream(recordPath, FileMode.Open, FileAccess.Read);
50-
var authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream);
51-
credOptions.AuthenticationRecord = authRecord;
52-
}
42+
TokenCachePersistenceOptions tokenCacheOptions = new() { Name = Constants.TokenCacheName };
43+
credOptions.TokenCachePersistenceOptions = tokenCacheOptions;
44+
var recordPath = Constants.AuthRecordPath;
45+
46+
if (File.Exists(recordPath))
47+
{
48+
using var authRecordStream = new FileStream(recordPath, FileMode.Open, FileAccess.Read);
49+
var authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream);
50+
credOptions.AuthenticationRecord = authRecord;
5351
}
5452

5553
return new DeviceCodeCredential(credOptions);
5654
}
5755
}
58-
59-
enum AuthenticationStrategy {
60-
DeviceCode
61-
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Microsoft.Graph.Cli.Authentication;
2+
3+
public enum AuthenticationStrategy {
4+
DeviceCode
5+
}

src/Authentication/DeviceCodeAuthenticationService.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Azure.Core;
2+
using Azure.Identity;
3+
using Microsoft.Graph.Cli.Utils;
4+
using System.IO;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.Graph.Cli.Authentication;
8+
9+
class DeviceCodeLoginService : LoginServiceBase {
10+
private DeviceCodeCredential credential;
11+
12+
public DeviceCodeLoginService(DeviceCodeCredential credential) {
13+
this.credential = credential;
14+
}
15+
16+
protected override async Task<AuthenticationRecord> DoLoginAsync(string[] scopes) {
17+
return await credential.AuthenticateAsync(new TokenRequestContext(scopes));
18+
}
19+
}

src/Authentication/IAuthenticationService.cs renamed to src/Authentication/ILoginService.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Microsoft.Graph.Cli.Authentication;
44

5-
interface IAuthenticationService {
5+
interface ILoginService {
66
Task LoginAsync(string[] scopes);
7+
8+
void Logout();
79
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.IO;
2+
using System.Threading.Tasks;
3+
using Azure.Identity;
4+
using Microsoft.Graph.Cli.Utils;
5+
6+
namespace Microsoft.Graph.Cli.Authentication;
7+
8+
abstract class LoginServiceBase : ILoginService {
9+
public async Task LoginAsync(string[] scopes) {
10+
var record = await this.DoLoginAsync(scopes);
11+
await this.SaveSessionAsync(record);
12+
}
13+
14+
public void Logout() {
15+
if (File.Exists(Constants.AuthRecordPath)) {
16+
File.Delete(Constants.AuthRecordPath);
17+
}
18+
}
19+
20+
protected abstract Task<AuthenticationRecord> DoLoginAsync(string[] scopes);
21+
22+
public async Task SaveSessionAsync(AuthenticationRecord record) {
23+
using var authRecordStream = new FileStream(Constants.AuthRecordPath, FileMode.OpenOrCreate, FileAccess.Write);
24+
await record.SerializeAsync(authRecordStream);
25+
}
26+
}

src/Commands/Authentication/LoginCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public LoginCommand(AuthenticationServiceFactory authenticationServiceFactory) {
1313
this.authenticationServiceFactory = authenticationServiceFactory;
1414
}
1515

16-
public Command Build(bool persistToken = false) {
16+
public Command Build() {
1717
var loginCommand = new Command("login", "Login and store the session for use in subsequent commands");
1818
var scopes = new Option<string>("--scopes", "The login scopes e.g. User.Read");
1919
scopes.IsRequired = true;
@@ -25,7 +25,7 @@ public Command Build(bool persistToken = false) {
2525

2626
loginCommand.Handler = CommandHandler.Create<string[], AuthenticationStrategy>(async (scopes, strategy) =>
2727
{
28-
var authService = await this.authenticationServiceFactory.GetAuthenticationServiceAsync(strategy, persistToken);
28+
var authService = await this.authenticationServiceFactory.GetAuthenticationServiceAsync(strategy);
2929
await authService.LoginAsync(scopes);
3030
});
3131

src/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ class Program {
1414
static async Task<int> Main(string[] args) {
1515
var authServiceFactory = new AuthenticationServiceFactory();
1616
var authStrategy = AuthenticationStrategy.DeviceCode;
17-
var persistToken = true;
1817

19-
var credential = await authServiceFactory.GetTokenCredentialAsync(authStrategy, persistToken);
18+
var credential = await authServiceFactory.GetTokenCredentialAsync(authStrategy);
2019
var authProvider = new AzureIdentityAuthenticationProvider(credential);
2120
var core = new HttpClientRequestAdapter(authProvider);
2221
var client = new GraphClient(core);
@@ -25,7 +24,7 @@ static async Task<int> Main(string[] args) {
2524
rootCommand.Description = "Microsoft Graph CLI";
2625

2726
var loginCommand = new LoginCommand(authServiceFactory);
28-
rootCommand.AddCommand(loginCommand.Build(persistToken));
27+
rootCommand.AddCommand(loginCommand.Build());
2928
return await rootCommand.InvokeAsync(args);
3029
}
3130
}

0 commit comments

Comments
 (0)