async Task <(bool isAuthenticated, bool serviceIdentityFound)> AuthenticateWithAuthChain(T credentials, string actorDeviceId, bool syncServiceIdentity) { // The auth target is the first element of the authchain Option <string> authTargetOption = AuthChainHelpers.GetAuthTarget(credentials.AuthChain); string authTarget = authTargetOption.Expect(() => new InvalidOperationException("Credentials should always have a valid auth-chain for OnBehalfOf authentication")); // For nested Edge, we need to check that we have // a valid authchain for the target identity Option <string> authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget); if (!authChain.HasValue) { Events.NoAuthChain(authTarget); return(false, false); } // Check that the actor is authorized to connect OnBehalfOf of the target if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, authTarget, authChain.OrDefault())) { // We found the target identity in our cache, but can't proceed with auth Events.UnauthorizedAuthChain(actorDeviceId, authTarget, authChain.OrDefault()); return(false, true); } // Check credentials against the acting EdgeHub string actorEdgeHubId = actorDeviceId + $"/{Constants.EdgeHubModuleId}"; return(await this.AuthenticateWithServiceIdentity(credentials, actorEdgeHubId, syncServiceIdentity)); }
public async Task RefreshAuthChain(string authChain) { Preconditions.CheckNonWhiteSpace(authChain, nameof(authChain)); // Refresh each element in the auth-chain Events.RefreshingAuthChain(authChain); string[] ids = AuthChainHelpers.GetAuthChainIds(authChain); await this.RefreshServiceIdentities(ids); }
async Task <(bool isAuthenticated, bool shouldFallback)> AuthenticateInternalAsync(T tCredentials, bool reauthenticating) { try { if (!this.AreInputCredentialsValid(tCredentials)) { Events.InputCredentialsNotValid(tCredentials.Identity); return(false, false); } bool syncServiceIdentity = this.syncServiceIdentityOnFailure && !reauthenticating; bool isAuthenticated = false; bool valueFound = false; // Try to get the actor from the client auth chain, this will only be valid for OnBehalfOf authentication Option <string> onBehalfOfActorDeviceId = AuthChainHelpers.GetActorDeviceId(tCredentials.AuthChain); if (this.nestedEdgeEnabled && onBehalfOfActorDeviceId.HasValue) { // OnBehalfOf means an EdgeHub is trying to use its own auth to // connect as a child device or module. So if acting EdgeHub is // different than the target identity, then we know we're // processing an OnBehalfOf authentication. (isAuthenticated, valueFound) = await this.AuthenticateWithAuthChain(tCredentials, onBehalfOfActorDeviceId.OrDefault(), syncServiceIdentity); } else { // Default scenario where the credential is for the target identity (isAuthenticated, valueFound) = await this.AuthenticateWithServiceIdentity(tCredentials, tCredentials.Identity.Id, syncServiceIdentity); if (!isAuthenticated && this.allowDeviceAuthForModule && tCredentials.Identity is IModuleIdentity moduleIdentity) { // The module could have used the Device key to sign its token Events.AuthenticatingWithDeviceIdentity(moduleIdentity); (isAuthenticated, valueFound) = await this.AuthenticateWithServiceIdentity(tCredentials, moduleIdentity.DeviceId, syncServiceIdentity); } } // In the return value, the first flag indicates if the authentication succeeded. // The second flag indicates whether the authenticator should fall back to the underlying authenticator. This is done if // the ServiceIdentity was not found (which means the device/module is not in scope). return(isAuthenticated, !valueFound); } catch (Exception e) { Events.ErrorAuthenticating(e, tCredentials, reauthenticating); return(false, true); } }
async Task <(bool isAuthenticated, bool serviceIdentityFound)> AuthenticateWithAuthChain(T credentials, string actorDeviceId, bool syncServiceIdentity) { // The auth target is the first element of the authchain Option <string> authTargetOption = AuthChainHelpers.GetAuthTarget(credentials.AuthChain); string authTarget = authTargetOption.Expect(() => new InvalidOperationException("Credentials should always have a valid auth-chain for OnBehalfOf authentication")); // For nested Edge, we need to check that we have // a valid authchain for the target identity Option <string> authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget); if (!authChain.HasValue) { // The auth-target might be a new device that was recently added, and our // cache might not have it yet. Try refreshing the target identity to see // if we can get it from upstream. Events.NoAuthChainResyncing(authTarget, actorDeviceId); await this.deviceScopeIdentitiesCache.RefreshServiceIdentityOnBehalfOf(authTarget, actorDeviceId); authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget); if (!authChain.HasValue) { // Still don't have a valid auth-chain for the target, it must be // out of scope, so we're done here Events.NoAuthChain(authTarget); return(false, false); } } // Check that the actor is authorized to connect OnBehalfOf of the target if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, authTarget, authChain.OrDefault())) { // We found the target identity in our cache, but can't proceed with auth Events.UnauthorizedAuthChain(actorDeviceId, authTarget, authChain.OrDefault()); return(false, true); } // Check credentials against the acting EdgeHub, since we would have // already refreshed the target identity on failure, there's no need // to have AuthenticateWithServiceIdentity do it again. string actorEdgeHubId = actorDeviceId + $"/{Constants.EdgeHubModuleId}"; return(await this.AuthenticateWithServiceIdentity(credentials, actorEdgeHubId, false)); }
public async Task Add(IClientCredentials clientCredentials) { if (clientCredentials is ITokenCredentials tokenCredentials) { string targetId = AuthChainHelpers.GetAuthTarget(tokenCredentials.AuthChain).GetOrElse(tokenCredentials.Identity.Id); try { var tokenCredentialsData = new TokenCredentialsData(tokenCredentials.Token, tokenCredentials.IsUpdatable, tokenCredentials.AuthChain); string tokenCredentialsString = JsonConvert.SerializeObject(tokenCredentialsData); await this.encryptedStore.Put(targetId, tokenCredentialsString); Events.Stored(clientCredentials.Identity.Id); } catch (Exception e) { Events.ErrorStoring(e, clientCredentials.Identity.Id); } } }
void UpdateCachedCredentials(IClientCredentials clientCredentials) { string targetId = AuthChainHelpers.GetAuthTarget(clientCredentials.AuthChain).GetOrElse(clientCredentials.Identity.Id); if (clientCredentials.AuthChain.HasValue) { // OnBehalfOf connection from a child Edge, there should always be an actor device string actorDeviceId = AuthChainHelpers.GetActorDeviceId(clientCredentials.AuthChain) .Expect(() => new ArgumentException($"Invalid auth-chain: {clientCredentials.AuthChain.OrDefault()}")); // Only EdgeHub can act OnBehalfOf another identity string actorId = $"{actorDeviceId}/{Constants.EdgeHubModuleId}"; if (!this.onBehalfOfRelations.ContainsKey(actorId)) { // Create a new OnBehalfOf mapping for this actor Edge this.onBehalfOfRelations[actorId] = new HashSet <string>(); } // Update our books for the new relation this.onBehalfOfRelations[actorId].Add(targetId); } else { // If there's no auth-chain, then this could be a credential update // for a child EdgeHub acting OnBehalfOf other identities if (this.onBehalfOfRelations.TryGetValue(clientCredentials.Identity.Id, out HashSet <string> onBehalfOfClients)) { // Need to update all clients that the child EdgeHub is acting OnBehalfOf foreach (string client in onBehalfOfClients) { IClientCredentials updatedCredentials = GetCredentialsWithOnBehalfOfUpdates(this.credentials[client], clientCredentials); this.credentials[client] = updatedCredentials; } } } // Update the client credentials this.credentials[targetId] = clientCredentials; }
async Task <string> VerifyServiceIdentityAuthChainState(string id, bool refreshCachedIdentity = false) { if (refreshCachedIdentity) { Events.RefreshingServiceIdentity(id); var authChainTry = await this.serviceIdentityHierarchy.TryGetAuthChain(id); var onBehalfOfDeviceId = AuthChainHelpers.GetAuthParent(authChainTry.Ok()); await this.RefreshServiceIdentityOnBehalfOf(id, onBehalfOfDeviceId.GetOrElse(this.edgeDeviceId)); } string authChain = (await this.serviceIdentityHierarchy.TryGetAuthChain(id)).Value; Option <ServiceIdentity> serviceIdentity = await this.GetServiceIdentity(id); this.VerifyServiceIdentity(serviceIdentity.Expect(() => { Events.VerifyServiceIdentityFailure(id, "Device is out of scope."); return(new DeviceInvalidStateException("Device is out of scope.")); })); return(authChain); }