I
have been following the DotnetOPenAuth example of the oAuth authorization.
on the OAuth2AuthorizeAttribute i am always getting "Missing Access Token."
I have checked again and again i am passing the access token via the header and its been set nicely within the request object.
What could be the reason for that?
Here is the code
protected virtual bool IsAuthorized(HttpActionContext actionContext, out IPrincipal user)
{
var signingKey = ApplicationSettings.SigningKey(ApplicationSettings.KeyType.Public);
var resourceKey = ApplicationSettings.ResoureKey(ApplicationSettings.KeyType.Private);
using (var signing = signingKey)
using (var resource = resourceKey)
{
base.OnAuthorization(actionContext);
// TODO FIXME dnoa doesn't support HttpRequestMessage - manually creating HttpRequestMessageProperty until they do
var request = new HttpRequestMessageProperty();
if (actionContext.Request.Headers.Authorization != null)
{
request.Headers[HttpRequestHeader.Authorization] =
actionContext.Request.Headers.Authorization.ToString();
}
else
{
request.Headers[HttpRequestHeader.Authorization] = null;
}
var requestUri = actionContext.Request.RequestUri;
var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(signing, resource));
try
{
user = resourceServer.GetPrincipal(request, requestUri, _oauth2Scopes);
return true;
}
catch (ProtocolFaultResponseException x)
{
user = null;
return false;
}
}
}
I found the problem.
In the Authorization header need to have the text Bearer before the access token for example
Bearer gAAAALfeAiFpUFOY8bJggyQ
Related
My controller code is lke this,
public async Task<IEnumerable<CalendarEvent>> Get()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "xxxxx";
var clientId = "xxxxxx";
var clientSecret = "xxxx";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes);
if (User == null!)
{
var user = await graphServiceClient.Users["xxxxx.com"].Calendar
.Events
.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location")
.GetAsync();
return (CalendarEvent)user;
}
}
Iam getting an error like
Unable to cast object of type 'Microsoft.Graph.CalendarEventsCollectionPage' to type 'System.Collections.Generic.IEnumerable'
I need query that sholud be given in controller.
It's not exactly clear what you are trying to achieve but you can't convert CalendarEventsCollectionPage to IEnumerable. I am assuming that you want to return all events of specific user.
public async Task<List<Event>> GetEventsOfUser(string userId)
{
var events = new List<Event>();
var eventsPages = _client.Users[userId].Calendar.Events.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location");
while (eventsPages != null)
{
var current = await eventsPages.GetAsync();
events.AddRange(current.CurrentPage);
eventsPages = current.NextPageRequest;
}
return events;
}
You need to fetch every page with NextPageRequest in order to get all events.
I have a background service which reads & sends from a mailbox. It is created in a web ui, but after the schedule is created and mailbox set, it should run automatically, without further user prompt.
I have used the various combinations of the MSAL and both public and confidential clients (either would be acceptable as the server can maintain the client secret.
I have used the EWS client and got that working, but there is a note that the client_credentials flow won't work for IMAP/POP/SMTP.
I have a small console app working, but each time it runs, it needs to login interactively, and so long as I don't restart the application, it will keep authenticating, and I can call the AquireTokenSilently.
The Question
How can I make the MSAL save the tokens/data such that when it next runs, I can authenticate without user interaction again? I can store whatever is needed to make this work when the user authenticates, but I don't know what that should be nor how to reinstate it to make a new request, if the console app is restarted.
The Code
internal async Task<string> Test()
{
PublicClientApplication =
PublicClientApplicationBuilder.Create( "5896de31-e251-460c-9dc2-xxxxxxxxxxxx" )
.WithRedirectUri( "https://login.microsoftonline.com/common/oauth2/nativeclient" )
.WithAuthority( AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings["tenantId"] )
.Build();
//var scopes = new string[] { "email", "offline_access", "profile", "User.Read", "Mail.Read" };
var scopes = new string[] { "https://outlook.office.com/IMAP.AccessAsUser.All" };
var accounts = await PublicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
AuthenticationResult authResult;
if (firstAccount == null )
{
authResult = await PublicClientApplication.AcquireTokenInteractive( scopes ).ExecuteAsync();
}
else
{
//The firstAccount is null when the console app is run again
authResult = await PublicClientApplication.AcquireTokenSilent( scopes, firstAccount ).ExecuteAsync();
}
if(authResult == null)
{
authResult = await PublicClientApplication.AcquireTokenInteractive( scopes ).ExecuteAsync();
}
MailBee.Global.LicenseKey = "MN120-569E9E8D9E5B9E8D9EC8C4BC83D3-D428"; // (demo licence only)
MailBee.ImapMail.Imap imap = new MailBee.ImapMail.Imap();
var xOAuthkey = MailBee.OAuth2.GetXOAuthKeyStatic( authResult.Account.Username, authResult.AccessToken );
imap.Connect( "imap.outlook.com", 993 );
imap.Login( null, xOAuthkey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, null );
imap.SelectFolder( "INBOX" );
var count = imap.MessageCount.ToString();
return authResult.AccessToken;
}
It feels very much like a step missed, which can store the information to make subsequent requests and I would love a pointer in the right direction please.
When you create your PublicClientApplication, it provides you with the UserTokenCache.
UserTokenCache implements interface ITokenCache, which defines events to subscribe to token cache serialization requests as well as methods to serialize or de-serialize the cache at various formats.
You should create your own TokenCacheBuilder, which can store the tokens in file/memory/database etc.. and then use the events to subscribe to to token cache request.
An example of a FileTokenCacheProvider:
public abstract class MsalTokenCacheProviderBase
{
private Microsoft.Identity.Client.ITokenCache cache;
private bool initialized = false;
public MsalTokenCacheProviderBase()
{
}
public void InitializeCache(Microsoft.Identity.Client.ITokenCache tokenCache)
{
if (initialized)
return;
cache = tokenCache;
cache.SetBeforeAccessAsync(OnBeforeAccessAsync);
cache.SetAfterAccessAsync(OnAfterAccessAsync);
initialized = true;
}
private async Task OnAfterAccessAsync(TokenCacheNotificationArgs args)
{
if (args.HasStateChanged)
{
if (args.HasTokens)
{
await StoreAsync(args.Account.HomeAccountId.Identifier,
args.TokenCache.SerializeMsalV3()).ConfigureAwait(false);
}
else
{
// No token in the cache. we can remove the cache entry
await DeleteAsync<bool>(args.SuggestedCacheKey).ConfigureAwait(false);
}
}
}
private async Task OnBeforeAccessAsync(TokenCacheNotificationArgs args)
{
if (!string.IsNullOrEmpty(args.SuggestedCacheKey))
{
byte[] tokenCacheBytes = await GetAsync<byte[]>(args.SuggestedCacheKey).ConfigureAwait(false);
args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache: true);
}
}
protected virtual Task OnBeforeWriteAsync(TokenCacheNotificationArgs args)
{
return Task.CompletedTask;
}
public abstract Task StoreAsync<T>(string key, T value);
public abstract Task DeleteAsync<T>(string key);
public abstract Task<T> GetAsync<T>(string key);
public abstract Task ClearAsync();
}
And the MsalFileTokenCacheProvider:
public sealed class MsalFileTokenCacheProvider : MsalTokenCacheProviderBase
{
private string basePath;
public MsalFileTokenCacheProvider(string basePath)
{
this.basePath = basePath;
}
public override Task ClearAsync()
{
throw new NotImplementedException();
}
public override Task DeleteAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
string path = Path.Combine(basePath, key + ".json");
if (File.Exists(path))
File.Delete(path);
return Task.FromResult(true);
}
public override Task<T> GetAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
string path = Path.Combine(basePath, key + ".json");
if (File.Exists(path))
{
T value = JsonConvert.DeserializeObject<T>(File.ReadAllText(path));
return Task.FromResult(value);
}
else
return Task.FromResult(default(T));
}
public override Task StoreAsync<T>(string key, T value)
{
string contents = JsonConvert.SerializeObject(value);
string path = Path.Combine(basePath, key + ".json");
File.WriteAllText(path, contents);
return Task.FromResult(value);
}
}
So based on your code you will have:
PublicClientApplication =
PublicClientApplicationBuilder.Create( "5896de31-e251-460c-9dc2-xxxxxxxxxxxx" )
.WithRedirectUri( "https://login.microsoftonline.com/common/oauth2/nativeclient" )
.WithAuthority( AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings["tenantId"] )
.Build();
MsalFileTokenCacheProvider cacheProvider = new MsalFileTokenCacheProvider("TokensFolder");
cacheProvider.InitializeCache(PublicClientApplication.UserTokenCache);
//var scopes = new string[] { "email", "offline_access", "profile", "User.Read", "Mail.Read" };
var scopes = new string[] { "https://outlook.office.com/IMAP.AccessAsUser.All" };
// when you call the below code, the PublicClientApplication will use your token cache
//provider in order to get the required Account. You should also use the
//PublicClientApplication.GetAccountAsync(key) which will use the token cache provider for
//the specific account that you want to get the token. If there is an account you could
//just call the AcquireTokenSilent method. The acquireTokenSilent method will take care of the token expiration and will refresh if needed.
//Please bare in mind that in some circumstances the AcquireTokenSilent method will fail and you will have to use the AcquireTokenInteractive method again. //Example of this would be when the user changes password, or has removed the access to your Application via their Account.
var accounts = await PublicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
Please refer to the following documentation from Microsoft.
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-net-token-cache-serialization
When I make rememberMe(spring security) active in Hybris, I want rememberMe users to behave like Hard Login users. I mean, I would like those rememberMe users (soft login) to not face any obstacle like when they want to proceed in the checkout process or something like that. How can I achieve this in SAP Hybris platform?
Your question: How to disable HardLogin for the remember-me user in Hybris?
find the detail explanation here
Change RequireHardLoginBeforeControllerHandler
Change beforeController method of RequireHardLoginBeforeControllerHandler.java, so that it always check if remember-me cookies present in the request and guid is missing or invalidated then create new guid without redirecting login page.
Below yourstorefrontRememberMe needs to change with your storefront name, like mySiteRemmberMe
public static final String SECURE_REMEMBER_ME_COOKIES = "yourstorefrontRememberMe";
#Resource(name = "guidCookieStrategy")
private GUIDCookieStrategy guidCookieStrategy;
#Override
public boolean beforeController(final HttpServletRequest request, final HttpServletResponse response,
final HandlerMethod handler) throws Exception
{
boolean redirect = true;
// We only care if the request is secure
if (request.isSecure())
{
// Check if the handler has our annotation
final RequireHardLogIn annotation = findAnnotation(handler, RequireHardLogIn.class);
if (annotation != null)
{
final String guid = (String) request.getSession().getAttribute(SECURE_GUID_SESSION_KEY);
if ((!getUserService().isAnonymousUser(getUserService().getCurrentUser()) || checkForAnonymousCheckout()) &&
checkForGUIDCookie(request, response, guid))
{
redirect = false;
}
if (redirect)
{
if(isRememberMeCookiePresent(request))
{
// If you find your guid is missing, lets recreate it.
guidCookieStrategy.setCookie(request, response);
return true;
}
else
{
LOG.warn((guid == null ? "missing secure token in session" : "no matching guid cookie") + ", redirecting");
getRedirectStrategy().sendRedirect(request, response, getRedirectUrl(request));
return false;
}
}
}
}
return true;
}
protected boolean isRememberMeCookiePresent(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if ((cookies == null) || (cookies.length == 0)) {
return false;
}
for (Cookie cookie : cookies) {
if (SECURE_REMEMBER_ME_COOKIES.equals(cookie.getName())) {
return cookie.getValue() != null;
}
}
return false;
}
please tell me how correct parse JWT token.
I needed get roles...
static Authentication getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
List<String> roles = (ArrayList<String>)claims.get("roles");
System.out.println("ROLE IS: " + roles);
return user != null ?
new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()) :
null;
}
return null;
}
output ROLE IS: [{authority=ROLE_ADMIN}, {authority=ROLE_USER}
How correct i can doing cast to Collection for use as parameter in UsernamePasswordAuthenticationToken.
Thank you.
So the problem was solved...
List<String> roles = (ArrayList)claims.get("roles");
for (Object role:roles){
HashMap<String,String> map = (HashMap<String,String>)role;
List<String> list = new ArrayList<>();
list.addAll(map.values());
for (String item: list) {
grantedAuthorityList = AuthorityUtils.createAuthorityList(item);
}
i have been searching on the net from last 2 days for MVC Implementation of OAuthConsumer Sample in DotNetOpenAuth, but still i did not found any solution.
i had also tried to convert OAuthConsumer implementation from WebForms to MVC, but still unable to implement it correctly.
can anybody please help by referring some place to find a converter sample.
After 2 days of struggle I resolved the issue as follows, but I think it needs some more improvement.
private string AccessToken
{
get { return (string)Session["GoogleAccessToken"]; }
set { Session["GoogleAccessToken"] = value; }
}
private InMemoryTokenManager TokenManager
{
get
{
var tokenManager = (InMemoryTokenManager)HttpContext.Application["GoogleTokenManager"];
if (tokenManager == null)
{
string consumerKey = ConfigurationManager.AppSettings["GoogleOAuthConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["GoogleOAuthConsumerValue"];
if (!string.IsNullOrEmpty(consumerKey))
{
tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
HttpContext.Application["GoogleTokenManager"] = tokenManager;
}
}
return tokenManager;
}
}
public ActionResult GoogleSync()
{
var google = new WebConsumer(GoogleConsumer.ServiceDescription, this.TokenManager);
// Is Google calling back with authorization?
var accessTokenResponse = google.ProcessUserAuthorization();
if (accessTokenResponse != null)
{
this.AccessToken = accessTokenResponse.AccessToken;
XDocument contactsDocument = GoogleConsumer.GetContacts(google, this.AccessToken, 5, 1);
var contactList = new List<GMailContact>();
foreach (var entry in contactsDocument.Root.Elements(XName.Get("entry", "http://www.w3.org/2005/Atom")))
{
GMailContact newContact = new GMailContact { Name = string.Empty, Email = string.Empty };
var titleElement = entry.Element(XName.Get("title", "http://www.w3.org/2005/Atom"));
if (titleElement != null)
newContact.Name = titleElement.Value;
var emailElement = entry.Element(XName.Get("email", "http://schemas.google.com/g/2005"));
if (emailElement != null && emailElement.Attribute("address") != null)
{
newContact.Email = emailElement.Attribute("address").Value;
}
contactList.Add(newContact);
}
////var contacts = from entry in contactsDocument.Root.Elements(XName.Get("entry", "http://www.w3.org/2005/Atom"))
//// select new { Name = entry.Element(XName.Get("title", "http://www.w3.org/2005/Atom")).Value,
//// Email = (XName.Get("email", "http://schemas.google.com/g/2005") == null ? "" : entry.Element(XName.Get("email", "http://schemas.google.com/g/2005")).Attribute("address").Value) };
return View(contactList);
}
else if (this.AccessToken == null)
{
// If we don't yet have access, immediately request it.
GoogleConsumer.RequestAuthorization(google, GoogleConsumer.Applications.Contacts);
return this.Content("");
}
else
{
return this.Content("synchronization failed.");
}
}
There isn't any MVC sample of an OAuth Consumer that I'm aware of. But since an OAuth consumer really has nothing to do with the presentation framework it shouldn't be any different between web forms and MVC. You should be able to just lift the consumer-related code directly out of the web forms sample and have it work in MVC.
If that doesn't work, please add more to your question that explains the problems you're seeing.