I am using SwiftHTTP and try to get a JSON Feed from my Server. The Server has a self signed Certificate and Access via User and Password.
I tryed with Alamofire but I found no Solution
here's my Code
HTTP.globalRequest { req in
req.timeoutInterval = 5
}
//set a global SSL pinning setting
HTTP.globalSecurity(HTTPSecurity()) //see the SSL section for more info
//set global auth handler. See the Auth section for more info
HTTP.globalAuth { challenge in
return URLCredential(user: "user", password: "passwd", persistence: .forSession)
}
do {
let opt = try HTTP.GET("https://10.0.1.2:4711/fhem/?cmd=jsonlist2&XHR=1",requestSerializer: JSONParameterSerializer())
//the auth closures will continually be called until a successful auth or rejection
var attempted = false
opt.auth = { challenge in
if !attempted {
attempted = true
return URLCredential(forTrust: challenge.proposedCredential?.certificates)
}
return nil
}
opt.start { response in
print("success")
print("opt finished: \(response.data)")
}
} catch let error {
print("got an error creating the request: \(error)")
}
}
Anyone an Idea ??
Related
I am trying to work with a gRPC api and I have to send credentials securely. I am having issues to figure this out. I am using the swift-grpc library. I will link the docs and maybe someone can explain what I am supposed to do.
I am still unsure of what makes this secure through ssl(are we sending certificates).
docs from the swift-grpc using tlc library here
If anyone can give an explenation of how the ssl works and what to do that would be great
// code
import Foundation
import GRPC
import NIO
class Networking {
var authServiceClient: PartnerApi2_PartnerApiClient?
let port: Int = 50052
init() {
// build a fountain of EventLoops
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
do {
// open a channel to the gPRC server
let channel = try GRPCChannelPool.with(
target: .host("partner-api.com", port: self.port),
transportSecurity: .plaintext,
eventLoopGroup: eventLoopGroup
)
// create a Client
self.authServiceClient = PartnerApi2_PartnerApiClient.init(channel: channel) //AuthService_AuthServiceRoutesClient(channel: channel)
print("grpc connection initialized")
login(username: "email", password: "password")
} catch {
print("Couldn’t connect to gRPC server")
}
}
func login(username: String, password: String) -> String {
print("Login: username=\(username)")
// build the AccountCredentials object
let accountCredentials: PartnerApi2_AuthenticationRequest = .with {
$0.partnerEmail = username
$0.password = password
}
// grab the login() method from the gRPC client
let call = self.authServiceClient!.authenticate(accountCredentials)
// prepare an empty response object
let oauthCredentials: PartnerApi2_AuthenticationResponse
// execute the gRPC call and grab the result
do {
oauthCredentials = try call.response.wait()
} catch {
print("RPC method ‘login’ failed: \(error)")
// it would be better to throw an error here, but
// let’s keep this simple for demo purposes
return ""
}
// Do something interesting with the result
let oauthToken = oauthCredentials.authToken
print("Logged in with oauth token \(oauthToken)")
// return a value so we can use it in the app
return oauthToken
}
}
I am using the swift package AWSiOSSDKV2 from https://github.com/aws-amplify/aws-sdk-ios-spm
I can log in using the SDK but how can I use refresh Token to generate new accessToken using their SDK?
func login() {
let pool = AWSCognitoIdentityUserPool(forKey: PortalUserConfig.POOL_NAME)
if let user = pool?.getUser() {
user.getSession(username, password: password, validationData: nil).continueWith(block: { task -> Any? in
if let error = task.error {
let errorMessage = (error as NSError).userInfo["message"] as? String
} else {
guard let accessToken = task.result?.accessToken else {
return nil
}
print(accessToken)
let refreshToken = task.result?.refreshToken
print(refreshToken)
}
return nil
})
}
}
The Amplify library will automatically fetch new id and access tokens if they have expired and the refresh token is still valid. You can adjust the lifetime of your refresh tokens in the settings for your User Pool client. To learn more about OAuth token lifetime strategies, you can read up on the options over at OAuth.com.
There are some similar question however none of those solve my problem.
Using Xcode 11.1
MacOS Cataline 10.15
I clone this "active-directory-b2c-ios-swift-native-msal" and try to run getting Error "Could not acquire token: Error Domain=MSALErrorDomain Code=-50000 "(null)" UserInfo={MSALErrorDescriptionKey=Failed to start an interactive session, MSALInternalErrorCodeKey=-42008, MSALCorrelationIDKey=C9207A45-6A7D-416B-90E4-93E08F28A637}"
After changing B2C details same issue getting .
Please let me know what is issue, Is this issue for Xcode/OS/MSAL version or some issue with Code??
I tried with default configuration mention in git repo "active-directory-b2c-ios-swift-native-msal" and also tried after below changed let kTenantName = "dovervsg.onmicrosoft.com" // Your tenant name
let kClientID = "xxxxxxxxxxxxxxxxxxxxxxx" // Your client ID from the portal when you created your application
let kSignupOrSigninPolicy = "B2C_1-policy" // Your signup and sign-in policy you created in the portal
let kEditProfilePolicy = "b2c_1_edit_profile" // Your edit policy you created in the portal
let kResetPasswordPolicy = "B2C_1_reset_password" // Your reset password policy you created in the portal
let kGraphURI = "https://dev-vsg.dovertech.co.in" // This is your backend API that you've configured to accept your app's tokens
let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // This is a scope that you've configured your backend API to look for.
// tried with this scope format as well, let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // This is a scope that you've configured your backend API to look for.
let kTenantName = "dovervsg.onmicrosoft.com" // Your tenant name
let kClientID = "xxxxxxxxxxxxxxxxxxxxxxx" // Your client ID from the portal when you created your application
let kSignupOrSigninPolicy = "B2C_1-policy" // Your signup and sign-in policy you created in the portal
let kEditProfilePolicy = "b2c_1_edit_profile" // Your edit policy you created in the portal
let kResetPasswordPolicy = "B2C_1_reset_password" // Your reset password policy you created in the portal
let kGraphURI = "https://dev-vsg.dovertech.co.in" // This is your backend API that you've configured to accept your app's tokens
let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // This is a scope that you've configured your backend API to look for.
// tried with this scope format as well, let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // This is a scope that you've configured your backend API to look for
// DO NOT CHANGE - This is the format of OIDC Token and Authorization endpoints for Azure AD B2C.
let kEndpoint = "https://login.microsoftonline.com/tfp/%#/%#"
var application: MSALPublicClientApplication!
var accessToken: String?
#IBOutlet weak var loggingText: UITextView!
#IBOutlet weak var signoutButton: UIButton!
#IBOutlet weak var callGraphApiButton: UIButton!
#IBOutlet weak var editProfileButton: UIButton!
#IBOutlet weak var refreshTokenButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
//super.viewDidLoad()
do {
/**
Initialize a MSALPublicClientApplication with a MSALPublicClientApplicationConfig.
MSALPublicClientApplicationConfig can be initialized with client id, redirect uri and authority.
Redirect uri will be constucted automatically in the form of "msal<your-client-id-here>://auth" if not provided.
The scheme part, i.e. "msal<your-client-id-here>", needs to be registered in the info.plist of the project
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
// Provide configuration for MSALPublicClientApplication
// MSAL will use default redirect uri when you provide nil
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: authority)
self.application = try MSALPublicClientApplication(configuration: pcaConfig)
} catch {
self.updateLoggingText(text: "Unable to create application \(error)")
}
}
/**
This button will invoke the authorization flow and send the policy specified to the B2C server.
Here we are using the `kSignupOrSignInPolicy` to sign the user in to the app. We will store this
accessToken for subsequent calls.
*/
#IBAction func authorizationButton(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
/**
Acquire a token for a new account using interactive authentication
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
let webViewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.promptType = .selectAccount
print( parameters.promptType = .selectAccount)
parameters.authority = authority
debugPrint( parameters.authority = authority)
application.acquireToken(with: parameters) { (result, error) in
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: \(error ?? "No error informarion" as! Error)")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Access token is \(self.accessToken ?? "Empty")")
self.signoutButton.isEnabled = true
self.callGraphApiButton.isEnabled = true
self.editProfileButton.isEnabled = true
self.refreshTokenButton.isEnabled = true
}
} catch {
self.updateLoggingText(text: "Unable to create authority \(error)")
}
}
#IBAction func editProfile(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)
/**
Acquire a token for a new account using interactive authentication
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kEditProfilePolicy)
let webViewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.authority = authority
parameters.account = thisAccount
application.acquireToken(with: parameters) { (result, error) in
if let error = error {
self.updateLoggingText(text: "Could not edit profile: \(error)")
} else {
self.updateLoggingText(text: "Successfully edited profile")
}
}
} catch {
self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
}
}
#IBAction func refreshToken(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
/**
Acquire a token for an existing account silently
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- account: An account object that we retrieved from the application object before that the
authentication flow will be locked down to.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
guard let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy) else {
self.updateLoggingText(text: "There is no account available!")
return
}
let parameters = MSALSilentTokenParameters(scopes: kScopes, account:thisAccount)
parameters.authority = authority
self.application.acquireTokenSilent(with: parameters) { (result, error) in
if let error = error {
let nsError = error as NSError
// interactionRequired means we need to ask the user to sign-in. This usually happens
// when the user's Refresh Token is expired or if the user has changed their password
// among other possible reasons.
if (nsError.domain == MSALErrorDomain) {
if (nsError.code == MSALError.interactionRequired.rawValue) {
// Notice we supply the account here. This ensures we acquire token for the same account
// as we originally authenticated.
let webviewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: self.kScopes, webviewParameters: webviewParameters)
parameters.account = thisAccount
self.application.acquireToken(with: parameters) { (result, error) in
guard let result = result else {
self.updateLoggingText(text: "Could not acquire new token: \(error ?? "No error informarion" as! Error)")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Access token is \(self.accessToken ?? "empty")")
}
return
}
}
self.updateLoggingText(text: "Could not acquire token: \(error)")
return
}
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: No result returned")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Refreshing token silently")
self.updateLoggingText(text: "Refreshed access token is \(self.accessToken ?? "empty")")
}
} catch {
self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
}
}
#IBAction func callApi(_ sender: UIButton) {
guard let accessToken = self.accessToken else {
self.updateLoggingText(text: "Operation failed because could not find an access token!")
return
}
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 30
let url = URL(string: self.kGraphURI)
var request = URLRequest(url: url!)
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
let urlSession = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
self.updateLoggingText(text: "Calling the API....")
urlSession.dataTask(with: request) { data, response, error in
guard let validData = data else {
self.updateLoggingText(text: "Could not call API: \(error ?? "No error informarion" as! Error)")
return
}
let result = try? JSONSerialization.jsonObject(with: validData, options: [])
guard let validResult = result as? [String: Any] else {
self.updateLoggingText(text: "Nothing returned from API")
return
}
self.updateLoggingText(text: "API response: \(validResult.debugDescription)")
}.resume()
}
#IBAction func signoutButton(_ sender: UIButton) {
do {
/**
Removes all tokens from the cache for this application for the provided account
- account: The account to remove from the cache
*/
let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy)
if let accountToRemove = thisAccount {
try application.remove(accountToRemove)
} else {
self.updateLoggingText(text: "There is no account to signing out!")
}
self.signoutButton.isEnabled = false
self.callGraphApiButton.isEnabled = false
self.editProfileButton.isEnabled = false
self.refreshTokenButton.isEnabled = false
self.updateLoggingText(text: "Signed out")
} catch {
self.updateLoggingText(text: "Received error signing out: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
if self.accessToken == nil {
signoutButton.isEnabled = false
callGraphApiButton.isEnabled = false
editProfileButton.isEnabled = false
refreshTokenButton.isEnabled = false
}
}
func getAccountByPolicy (withAccounts accounts: [MSALAccount], policy: String) throws -> MSALAccount? {
for account in accounts {
// This is a single account sample, so we only check the suffic part of the object id,
// where object id is in the form of <object id>-<policy>.
// For multi-account apps, the whole object id needs to be checked.
if let homeAccountId = account.homeAccountId, let objectId = homeAccountId.objectId {
if objectId.hasSuffix(policy.lowercased()) {
return account
}
}
}
return nil
}
/**
The way B2C knows what actions to perform for the user of the app is through the use of `Authority URL`.
It is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
func getAuthority(forPolicy policy: String) throws -> MSALB2CAuthority {
guard let authorityURL = URL(string: String(format: self.kEndpoint, self.kTenantName, policy)) else {
throw NSError(domain: "SomeDomain",
code: 1,
userInfo: ["errorDescription": "Unable to create authority URL!"])
}
return try MSALB2CAuthority(url: authorityURL)
}
func updateLoggingText(text: String) {
DispatchQueue.main.async{
self.loggingText.text = text
}
}
}
After run getting error above
The sample has been updated and should be working as expected now. It is now updated to handle *.b2clogin.com and now adds sui_si and edit profile to the known authorities list.
let siginPolicyAuthority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
let editProfileAuthority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)
// Provide configuration for MSALPublicClientApplication
// MSAL will use default redirect uri when you provide nil
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: siginPolicyAuthority)
pcaConfig.knownAuthorities = [siginPolicyAuthority, editProfileAuthority]
self.application = try MSALPublicClientApplication(configuration: pcaConfig)
and
func getAuthority(forPolicy policy: String) throws -> MSALB2CAuthority {
guard let authorityURL = URL(string: String(format: self.kEndpoint, self.kAuthorityHostName, self.kTenantName, policy)) else {
throw NSError(domain: "SomeDomain",
code: 1,
userInfo: ["errorDescription": "Unable to create authority URL!"])
}
return try MSALB2CAuthority(url: authorityURL)
}
I implemented a social network app that uses Cognito for refreshing token. But still, I can't get my new tokens. When I first log-in to my server and get my first ID and token from it, The token expires after a while and I can't get any token. This is my implementation:
class DeveloperAuthenticatedIdentityProvider : AWSCognitoCredentialsProviderHelper {
override func token() -> AWSTask<NSString> {
self.identityId = ProfileDAL.shared.getId()
return AWSTask(result: NSString(string: ProfileDAL.shared.getToken()))
}
override func logins() -> AWSTask<NSDictionary> {
return super.logins()
}
}
I put this lines in my viewDidLoad right after getting first token from my server
let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: MY_REGION, identityPoolId: MY_IDENTITY_POOL_ID, useEnhancedFlow: true, identityProviderManager:nil)
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: MY_REGION, identityProvider:devAuth)
let configuration = AWSServiceConfiguration(region: MY_REGION, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
and right after them, I use bellow lines to get my tokens:
AWSMobileClient.default().getTokens { (tokens, error) in
if let error = error {
print("Error getting token \(error.localizedDescription)")
} else if let tokens = tokens {
print(tokens.accessToken!.tokenString!)
}
}
finally I can't get my new tokens and it gives me this error:
AWSMobileClientError
▿ notSignedIn : 1 element
- message : "User is not signed in, please sign in to use this API."
Q & A Style: See Answer Below
How Can I get the username from a user logged in with Cognito?
I've done this and my user is logged in, now what?
AWSAuthUIViewController.presentViewController(
with: self.navigationController!,
configuration: config, completionHandler: { (provider: AWSSignInProvider, error: Error?) in
if error == nil {
//get parameters
}
} else {
print(error as Any)
}
})
}
Prerequisites:
App registered with MobileHub
Cognito Setup in MobileHub
Mobilehub integrated with Swift Project using AWS SDK
If you're like me, you did this with little to no difficulty and now you're stuck trying to get the username and other parameters from the logged in user. There are a lot of answers, but thus far, I haven't stumbled upon one that gets you all the way there.
I was able to piece this together from various sources:
func getUsername() {
//to check if user is logged in with Cognito... not sure if this is necessary
let identityManager = AWSIdentityManager.default()
let identityProvider = identityManager.credentialsProvider.identityProvider.identityProviderName
if identityProvider == "cognito-identity.amazonaws.com" {
print("************LOGGED IN WITH COGNITO************")
let serviceConfiguration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YourClientID", clientSecret: "YourSecretKey", poolId: "YourPoolID")
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "YourPoolName (typically formatted as YourAppName_userpoool_MOBILEHUB_12345678")
let pool = AWSCognitoIdentityUserPool(forKey: "YourPoolName")
// the following line doesn't seem to be necessary and isn't used so I've commented it out, but it is included in official documentation
// let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USWest2, identityPoolId: "YourPoolID", identityProviderManager:pool)
if let username = pool.currentUser()?.username {
print("Username Retrieved Successfully: \(username)")
} else {
print("Error getting username from current user - attempt to get user")
let user = pool.getUser()
let username = user.username
print("Username: \(username)")
}
}
}
To get your ClientID, Secret Key, and PoolID, check your awsconfiguration.json
To get your PoolName, login to MobileHub, and in your project's backend, go to User Sign in, click Email and Password, then click Edit in Cognito. The following page will have your Pool Name as "YourAppName_userpool_MOBILEHUB_12345678"
Edit: To get all of the attributes as well:
if let userFromPool = pool.currentUser() {
userFromPool.getDetails().continueOnSuccessWith(block: { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error as NSError? {
print("Error getting user attributes from Cognito: \(error)")
} else {
let response = task.result
if let userAttributes = response?.userAttributes {
print("user attributes found: \(userAttributes)")
for attribute in userAttributes {
if attribute.name == "email" {
if let email = attribute.value {
print("User Email: \(email)")
}
}
}
If you're using Cognito User Pools, you can use this:
import AWSUserPoolsSignIn
AWSCognitoUserPoolsSignInProvider.sharedInstance()
.getUserPool()
.currentUser()?
.username