1
- using Microsoft . AspNetCore . Mvc ;
1
+ using Microsoft . AspNetCore . Authorization ;
2
+ using Microsoft . AspNetCore . Mvc ;
2
3
using Microsoft . Extensions . Options ;
3
4
using Octokit ;
4
5
using Octokit . Internal ;
@@ -23,91 +24,113 @@ public record class SignInViewModel
23
24
}
24
25
25
26
[ ApiController ]
26
- public class AuthController ( IOptions < GitHubSettings > configuration , IOptions < ApiSettings > api , ILogger < AuthController > logger ) : ControllerBase
27
+ [ AllowAnonymous ]
28
+ public class AuthController : RubberduckApiController
27
29
{
30
+ private readonly IOptions < GitHubSettings > configuration ;
31
+
32
+ public AuthController ( IOptions < GitHubSettings > configuration , IOptions < ApiSettings > api , ILogger < AuthController > logger )
33
+ : base ( logger )
34
+ {
35
+ this . configuration = configuration ;
36
+ }
37
+
28
38
[ HttpGet ( "auth" ) ]
39
+ [ AllowAnonymous ]
29
40
public IActionResult Index ( )
30
41
{
31
- var claims = HttpContext . User . Claims . ToDictionary ( claim => claim . Type , claim => claim . Value ) ;
32
- var hasName = claims . TryGetValue ( ClaimTypes . Name , out var name ) ;
33
- var hasRole = claims . TryGetValue ( ClaimTypes . Role , out var role ) ;
34
-
35
- if ( hasName && hasRole )
42
+ return GuardInternalAction ( ( ) =>
36
43
{
37
- if ( string . IsNullOrEmpty ( name ) || string . IsNullOrWhiteSpace ( role ) )
44
+ var claims = HttpContext . User . Claims . ToDictionary ( claim => claim . Type , claim => claim . Value ) ;
45
+ var hasName = claims . TryGetValue ( ClaimTypes . Name , out var name ) ;
46
+ var hasRole = claims . TryGetValue ( ClaimTypes . Role , out var role ) ;
47
+
48
+ if ( hasName && hasRole )
38
49
{
39
- return BadRequest ( ) ;
50
+ if ( string . IsNullOrEmpty ( name ) || string . IsNullOrWhiteSpace ( role ) )
51
+ {
52
+ return BadRequest ( ) ;
53
+ }
54
+
55
+ var isAuthenticated = HttpContext . User . Identity ? . IsAuthenticated ?? false ;
56
+ var model = new UserViewModel
57
+ {
58
+ Name = name ,
59
+ IsAuthenticated = isAuthenticated ,
60
+ IsAdmin = role == configuration . Value . OwnerOrg
61
+ } ;
62
+
63
+ return Ok ( model ) ;
40
64
}
41
-
42
- var isAuthenticated = HttpContext . User . Identity ? . IsAuthenticated ?? false ;
43
- var model = new UserViewModel
65
+ else
44
66
{
45
- Name = name ,
46
- IsAuthenticated = isAuthenticated ,
47
- IsAdmin = role == configuration . Value . OwnerOrg
48
- } ;
49
-
50
- return Ok ( model ) ;
51
- }
52
- else
53
- {
54
- return Ok ( UserViewModel . Anonymous ) ;
55
- }
67
+ return Ok ( UserViewModel . Anonymous ) ;
68
+ }
69
+ } ) ;
56
70
}
57
71
58
72
[ HttpPost ( "auth/signin" ) ]
73
+ [ AllowAnonymous ]
59
74
public IActionResult SessionSignIn ( SignInViewModel vm )
60
75
{
61
- if ( User . Identity ? . IsAuthenticated ?? false )
76
+ return GuardInternalAction ( ( ) =>
62
77
{
63
- logger . LogInformation ( "Signin was requested, but user is already authenticated. Redirecting to home page..." ) ;
64
- return Redirect ( "/" ) ;
65
- }
78
+ if ( User . Identity ? . IsAuthenticated ?? false )
79
+ {
80
+ Logger . LogInformation ( "Signin was requested, but user is already authenticated. Redirecting to home page..." ) ;
81
+ return Redirect ( "/" ) ;
82
+ }
66
83
67
- var clientId = configuration . Value . ClientId ;
68
- var agent = configuration . Value . UserAgent ;
84
+ var clientId = configuration . Value . ClientId ;
85
+ var agent = configuration . Value . UserAgent ;
69
86
70
- var github = new GitHubClient ( new ProductHeaderValue ( agent ) ) ;
71
- var request = new OauthLoginRequest ( clientId )
72
- {
73
- AllowSignup = false ,
74
- Scopes = { "read:user" , "read:org" } ,
75
- State = vm . State
76
- } ;
77
-
78
- logger . LogInformation ( "Requesting OAuth app GitHub login url..." ) ;
79
- var url = github . Oauth . GetGitHubLoginUrl ( request ) ;
80
- if ( url is null )
81
- {
82
- logger . LogInformation ( "OAuth login was cancelled by the user or did not return a url." ) ;
83
- return Forbid ( ) ;
84
- }
87
+ var github = new GitHubClient ( new ProductHeaderValue ( agent ) ) ;
88
+ var request = new OauthLoginRequest ( clientId )
89
+ {
90
+ AllowSignup = false ,
91
+ Scopes = { "read:user" , "read:org" } ,
92
+ State = vm . State
93
+ } ;
85
94
86
- logger . LogInformation ( "Returning the login url for the client to redirect. State: {xsrf}" , vm . State ) ;
87
- return Ok ( url . ToString ( ) ) ;
95
+ Logger . LogInformation ( "Requesting OAuth app GitHub login url..." ) ;
96
+ var url = github . Oauth . GetGitHubLoginUrl ( request ) ;
97
+ if ( url is null )
98
+ {
99
+ Logger . LogInformation ( "OAuth login was cancelled by the user or did not return a url." ) ;
100
+ return Forbid ( ) ;
101
+ }
102
+
103
+ Logger . LogInformation ( "Returning the login url for the client to redirect. State: {xsrf}" , vm . State ) ;
104
+ return Ok ( url . ToString ( ) ) ;
105
+ } ) ;
88
106
}
89
107
90
108
[ HttpPost ( "auth/github" ) ]
91
- public async Task < IActionResult > OnGitHubCallback ( SignInViewModel vm )
109
+ [ AllowAnonymous ]
110
+ public IActionResult OnGitHubCallback ( SignInViewModel vm )
92
111
{
93
- logger . LogInformation ( "OAuth token was received. State: {state}" , vm . State ) ;
94
- var clientId = configuration . Value . ClientId ;
95
- var clientSecret = configuration . Value . ClientSecret ;
96
- var agent = configuration . Value . UserAgent ;
112
+ return GuardInternalAction ( ( ) =>
113
+ {
114
+ Logger . LogInformation ( "OAuth token was received. State: {state}" , vm . State ) ;
115
+ var clientId = configuration . Value . ClientId ;
116
+ var clientSecret = configuration . Value . ClientSecret ;
117
+ var agent = configuration . Value . UserAgent ;
97
118
98
- var github = new GitHubClient ( new ProductHeaderValue ( agent ) ) ;
119
+ var github = new GitHubClient ( new ProductHeaderValue ( agent ) ) ;
99
120
100
- var request = new OauthTokenRequest ( clientId , clientSecret , vm . Code ) ;
101
- var token = await github . Oauth . CreateAccessToken ( request ) ;
102
- if ( token is null )
103
- {
104
- logger . LogWarning ( "OAuth access token was not created." ) ;
105
- return Unauthorized ( ) ;
106
- }
121
+ var request = new OauthTokenRequest ( clientId , clientSecret , vm . Code ) ;
122
+ var token = github . Oauth . CreateAccessToken ( request ) . GetAwaiter ( ) . GetResult ( ) ;
123
+ if ( token is null )
124
+ {
125
+ Logger . LogWarning ( "OAuth access token was not created." ) ;
126
+ return Unauthorized ( ) ;
127
+ }
128
+
129
+ Logger . LogInformation ( "OAuth access token was created. Authorizing..." ) ;
130
+ var authorizedToken = AuthorizeAsync ( token . AccessToken ) . GetAwaiter ( ) . GetResult ( ) ;
107
131
108
- logger . LogInformation ( "OAuth access token was created. Authorizing..." ) ;
109
- var authorizedToken = await AuthorizeAsync ( token . AccessToken ) ;
110
- return authorizedToken is null ? Unauthorized ( ) : Ok ( vm with { Token = authorizedToken } ) ;
132
+ return authorizedToken is null ? Unauthorized ( ) : Ok ( vm with { Token = authorizedToken } ) ;
133
+ } ) ;
111
134
}
112
135
113
136
private async Task < string ? > AuthorizeAsync ( string token )
@@ -119,13 +142,13 @@ public async Task<IActionResult> OnGitHubCallback(SignInViewModel vm)
119
142
var githubUser = await github . User . Current ( ) ;
120
143
if ( githubUser . Suspended )
121
144
{
122
- logger . LogWarning ( "User {name} with login '{login}' ({url }) is a suspended GitHub account and will not be authorized." , githubUser . Name , githubUser . Login , githubUser . Url ) ;
145
+ Logger . LogWarning ( "User login '{login}' ({name }) is a suspended GitHub account and will not be authorized." , githubUser . Login , githubUser . Name ) ;
123
146
return default ;
124
147
}
125
148
126
149
var identity = new ClaimsIdentity ( "github" , ClaimTypes . Name , ClaimTypes . Role ) ;
127
150
identity . AddClaim ( new Claim ( ClaimTypes . Name , githubUser . Login ) ) ;
128
- logger . LogInformation ( "Creating claims identity for GitHub login '{login}'..." , githubUser . Login ) ;
151
+ Logger . LogInformation ( "Creating claims identity for GitHub login '{login}'..." , githubUser . Login ) ;
129
152
130
153
var orgs = await github . Organization . GetAllForUser ( githubUser . Login ) ;
131
154
var rdOrg = orgs . SingleOrDefault ( org => org . Id == configuration . Value . RubberduckOrgId ) ;
@@ -135,27 +158,27 @@ public async Task<IActionResult> OnGitHubCallback(SignInViewModel vm)
135
158
identity . AddClaim ( new Claim ( ClaimTypes . Role , configuration . Value . OwnerOrg ) ) ;
136
159
identity . AddClaim ( new Claim ( ClaimTypes . Authentication , token ) ) ;
137
160
identity . AddClaim ( new Claim ( "access_token" , token ) ) ;
138
- logger . LogDebug ( "GitHub Organization claims were granted. Creating claims principal..." ) ;
161
+ Logger . LogDebug ( "GitHub Organization claims were granted. Creating claims principal..." ) ;
139
162
140
163
var principal = new ClaimsPrincipal ( identity ) ;
141
164
var roles = string . Join ( "," , identity . Claims . Where ( claim => claim . Type == ClaimTypes . Role ) . Select ( claim => claim . Value ) ) ;
142
165
143
166
HttpContext . User = principal ;
144
167
Thread . CurrentPrincipal = HttpContext . User ;
145
168
146
- logger . LogInformation ( "GitHub user with login {login} has signed in with role authorizations '{role}'." , githubUser . Login , configuration . Value . OwnerOrg ) ;
169
+ Logger . LogInformation ( "GitHub user with login {login} has signed in with role authorizations '{role}'." , githubUser . Login , configuration . Value . OwnerOrg ) ;
147
170
return token ;
148
171
}
149
172
else
150
173
{
151
- logger . LogWarning ( "User {name} ({email}) with login '{login}' is not a member of organization ID {org} and will not be authorized." , githubUser . Name , githubUser . Email , githubUser . Login , configuration . Value . RubberduckOrgId ) ;
174
+ Logger . LogWarning ( "User {name} ({email}) with login '{login}' is not a member of organization ID {org} and will not be authorized." , githubUser . Name , githubUser . Email , githubUser . Login , configuration . Value . RubberduckOrgId ) ;
152
175
return default ;
153
176
}
154
177
}
155
178
catch ( Exception )
156
179
{
157
180
// just ignore: configuration needs the org (prod) client app id to avoid throwing this exception
158
- logger . LogWarning ( "An exception was thrown. Verify GitHub:ClientId and GitHub:ClientSecret configuration; authorization fails." ) ;
181
+ Logger . LogWarning ( "An exception was thrown. Verify GitHub:ClientId and GitHub:ClientSecret configuration; authorization fails." ) ;
159
182
return default ;
160
183
}
161
184
}
0 commit comments